From 867564337678f829fabea26172b2797a50efc7f3 Mon Sep 17 00:00:00 2001 From: Radhakrishna Sanka Date: Thu, 1 Jul 2021 06:16:10 -0400 Subject: [PATCH 01/36] fixed bug --- lfr/fig/fignode.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lfr/fig/fignode.py b/lfr/fig/fignode.py index 9044bf3..d24d134 100644 --- a/lfr/fig/fignode.py +++ b/lfr/fig/fignode.py @@ -36,7 +36,7 @@ def __eq__(self, other): if isinstance(other, FIGNode): return self.id == other.id else: - False + return False def rename(self, id: str) -> None: self._id = id From ebd69ff6d952a21bc4dabacde69c9443d4e30161 Mon Sep 17 00:00:00 2001 From: Radhakrishna Sanka Date: Mon, 1 Mar 2021 21:00:13 -0500 Subject: [PATCH 02/36] Created the interfaces and the base classes for the graph matcher --- generator-old.py | 952 +++++++++++++++++++++++++ lfr/graphmatch/__init__.py | 0 lfr/graphmatch/figmappingmatcher.py | 60 ++ lfr/graphmatch/interface.py | 50 ++ lfr/netlistgenerator/mappinglibrary.py | 11 +- lfr/netlistgenerator/primitive.py | 4 + notes.md | 259 +++++++ 7 files changed, 1334 insertions(+), 2 deletions(-) create mode 100644 generator-old.py create mode 100644 lfr/graphmatch/__init__.py create mode 100644 lfr/graphmatch/figmappingmatcher.py create mode 100644 lfr/graphmatch/interface.py create mode 100644 notes.md diff --git a/generator-old.py b/generator-old.py new file mode 100644 index 0000000..1f4d6ab --- /dev/null +++ b/generator-old.py @@ -0,0 +1,952 @@ +from lfr.netlistgenerator.v2.procedural_component_algorithms.ytree import YTREE +from lfr.netlistgenerator.v2.gen_strategies.dropxstrategy import DropXStrategy +from lfr.fig.fluidinteractiongraph import FluidInteractionGraph +from lfr.postprocessor.mapping import NetworkMapping, NodeMappingTemplate +from pymint.mintlayer import MINTLayerType +from lfr.netlistgenerator.primitive import NetworkPrimitive, Primitive, PrimitiveType +from lfr.netlistgenerator.v2.connectingoption import ConnectingOption +from lfr.netlistgenerator.mappinglibrary import MappingLibrary +from lfr.netlistgenerator.v2.networkmappingoption import ( + NetworkMappingOption, + NetworkMappingOptionType, +) +from lfr.netlistgenerator.v2.gen_strategies.genstrategy import GenStrategy +from lfr.fig.fignode import IOType, ValueNode +from typing import List +from pymint.mintdevice import MINTDevice +from lfr.netlistgenerator.namegenerator import NameGenerator +from lfr.netlistgenerator.v2.gen_strategies.dummy import DummyStrategy +from lfr.netlistgenerator.v2.constructionnode import ConstructionNode +from lfr.netlistgenerator.v2.constructiongraph import ConstructionGraph +from lfr.fig.interaction import ( + FluidIntegerInteraction, + FluidNumberInteraction, + InteractionType, +) +from lfr.netlistgenerator.v2.mappingoption import MappingOption +from lfr.compiler.module import Module +import networkx as nx + + +# def generate_MARS_library() -> MappingLibrary: +# # TODO - Programatically create each of the items necessary for the MARS primitive library, +# # we shall serialize them after experimentation + +# # mix_primitive = MappingOption() + + +# # mix_primitive.init_single_component(mint_component) + +# # mix_primitive.add +# pass + + +def generate_dropx_library() -> MappingLibrary: + + library = MappingLibrary("dropx") + + # PORT + port_inputs = [] + port_inputs.append(ConnectingOption(None, [None])) + port_inputs.append(ConnectingOption(None, [None])) + port_inputs.append(ConnectingOption(None, [None])) + port_inputs.append(ConnectingOption(None, [None])) + port_inputs.append(ConnectingOption(None, [None])) + port_inputs.append(ConnectingOption(None, [None])) + port_inputs.append(ConnectingOption(None, [None])) + port_inputs.append(ConnectingOption(None, [None])) + port_inputs.append(ConnectingOption(None, [None])) + port_inputs.append(ConnectingOption(None, [None])) + port_inputs.append(ConnectingOption(None, [None])) + port_inputs.append(ConnectingOption(None, [None])) + port_inputs.append(ConnectingOption(None, [None])) + port_inputs.append(ConnectingOption(None, [None])) + port_inputs.append(ConnectingOption(None, [None])) + port_inputs.append(ConnectingOption(None, [None])) + port_inputs.append(ConnectingOption(None, [None])) + port_inputs.append(ConnectingOption(None, [None])) + port_inputs.append(ConnectingOption(None, [None])) + + port_outputs = [] + port_outputs.append(ConnectingOption(None, [])) + port_outputs.append(ConnectingOption(None, [])) + port_outputs.append(ConnectingOption(None, [])) + port_outputs.append(ConnectingOption(None, [])) + port_outputs.append(ConnectingOption(None, [])) + port_outputs.append(ConnectingOption(None, [])) + port_outputs.append(ConnectingOption(None, [])) + port_outputs.append(ConnectingOption(None, [])) + port_outputs.append(ConnectingOption(None, [])) + port_outputs.append(ConnectingOption(None, [])) + port_outputs.append(ConnectingOption(None, [])) + port_outputs.append(ConnectingOption(None, [])) + port_outputs.append(ConnectingOption(None, [])) + port_outputs.append(ConnectingOption(None, [])) + port_outputs.append(ConnectingOption(None, [])) + port_outputs.append(ConnectingOption(None, [])) + port_outputs.append(ConnectingOption(None, [])) + port_outputs.append(ConnectingOption(None, [])) + port_outputs.append(ConnectingOption(None, [])) + + port = Primitive( + "PORT", + PrimitiveType.COMPONENT, + "IO", + False, + False, + port_inputs, + port_outputs, + None, + None, + None, + None, + None, + ) + + library.add_io_entry(port) + + # PICO INJECTOR + + pico_injector_inputs = [] + + pico_injector_inputs.append(ConnectingOption(None, ["1"])) + pico_injector_inputs.append(ConnectingOption(None, ["2"])) + + pico_injector_outputs = [] + + pico_injector_outputs.append(ConnectingOption(None, ["3"])) + + pico_injector_loadings = [] + pico_injector_carriers = [] + + pico_injector = Primitive( + "PICO INJECTOR", + PrimitiveType.COMPONENT, + "MIX", + False, + False, + pico_injector_inputs, + pico_injector_outputs, + pico_injector_loadings, + pico_injector_carriers, + None, + ) + + library.add_operator_entry(pico_injector, InteractionType.MIX) + + # DROPLET ELECTROPHORESIS MERGER + + electrophoresis_merger_inputs = [] + + electrophoresis_merger_inputs.append(ConnectingOption(None, ["1"])) + electrophoresis_merger_inputs.append(ConnectingOption(None, ["2"])) + + electrophoresis_merger_outputs = [] + + electrophoresis_merger_outputs.append(ConnectingOption(None, ["3"])) + + electrophoresis_merger_loadings = [] + electrophoresis_merger_carriers = [] + + electrophoresis_merger = Primitive( + "DROPLET ELECTROPHORESIS MERGER", + PrimitiveType.COMPONENT, + "MIX", + False, + False, + electrophoresis_merger_inputs, + electrophoresis_merger_outputs, + electrophoresis_merger_loadings, + electrophoresis_merger_carriers, + None, + ) + + library.add_operator_entry(electrophoresis_merger, InteractionType.MIX) + + # DROPLET GENERATOR + + droplet_generator_inputs = [] + + droplet_generator_inputs.append(ConnectingOption("default_component", ["1"])) + + droplet_generator_outputs = [] + + droplet_generator_outputs.append(ConnectingOption("default_component", ["3"])) + + droplet_generator_loadings = [] + droplet_generator_carriers = [] + + droplet_generator = Primitive( + "NOZZLE DROPLET GENERATOR", + PrimitiveType.NETLIST, + "METER", + False, + False, + droplet_generator_inputs, + droplet_generator_outputs, + droplet_generator_loadings, + droplet_generator_carriers, + "default-netlists/dropletgenerator.mint", + ["droplet_size", "generation_rate"], + [ + "orifice_size", + "aspect_ratio", + "capillary_number", + "expansion_ratio", + "flow_rate_ratio", + "normalized_oil_inlet", + "normalized_orifice_length", + "normalized_water_inlet", + ], + ) + + library.add_operator_entry(droplet_generator, InteractionType.METER) + + droplet_merger_junction_inputs = [] + + droplet_merger_junction_inputs.append(ConnectingOption(None, ["1"])) + droplet_merger_junction_inputs.append(ConnectingOption(None, ["2"])) + + droplet_merger_junction_outputs = [] + + droplet_merger_junction_outputs.append(ConnectingOption(None, ["3"])) + + droplet_merger_junction_loadings = [] + droplet_merger_junction_carriers = [] + + droplet_merger_junction = Primitive( + "DROPLET MERGER JUNCTION", + PrimitiveType.COMPONENT, + "MIX", + False, + False, + droplet_merger_junction_inputs, + droplet_merger_junction_outputs, + droplet_merger_junction_loadings, + droplet_merger_junction_carriers, + None, + ) + + library.add_operator_entry(droplet_merger_junction, InteractionType.MIX) + + # DROPLET MERGER CHANNEL + + droplet_merger_channel_inputs = [] + + droplet_merger_channel_inputs.append(ConnectingOption(None, ["1"])) + + droplet_merger_channel_outputs = [] + + droplet_merger_channel_outputs.append(ConnectingOption(None, ["2"])) + + droplet_merger_channel_loadings = [] + droplet_merger_channel_carriers = [] + + droplet_merger_channel = Primitive( + "DROPLET MERGER CHANNEL", + PrimitiveType.COMPONENT, + "MIX", + False, + False, + droplet_merger_channel_inputs, + droplet_merger_channel_outputs, + droplet_merger_channel_loadings, + droplet_merger_channel_carriers, + None, + ) + + library.add_operator_entry(droplet_merger_channel, InteractionType.MIX) + + # MIXER - CONTINOUS FLOW ONE + + cf_mixer_inputs = [] + + cf_mixer_inputs.append(ConnectingOption(None, ["1"])) + cf_mixer_inputs.append(ConnectingOption(None, ["1"])) + cf_mixer_inputs.append(ConnectingOption(None, ["1"])) + cf_mixer_inputs.append(ConnectingOption(None, ["1"])) + cf_mixer_inputs.append(ConnectingOption(None, ["1"])) + cf_mixer_inputs.append(ConnectingOption(None, ["1"])) + cf_mixer_inputs.append(ConnectingOption(None, ["1"])) + cf_mixer_inputs.append(ConnectingOption(None, ["1"])) + cf_mixer_inputs.append(ConnectingOption(None, ["1"])) + cf_mixer_inputs.append(ConnectingOption(None, ["1"])) + + cf_mixer_outputs = [] + + cf_mixer_outputs.append(ConnectingOption(None, ["2"])) + + cf_mixer_loadings = [] + cf_mixer_carriers = [] + + cf_mixer = Primitive( + "MIXER", + PrimitiveType.COMPONENT, + "MIX", + False, + False, + cf_mixer_inputs, + cf_mixer_outputs, + cf_mixer_loadings, + cf_mixer_carriers, + None, + ) + + library.add_operator_entry(cf_mixer, InteractionType.MIX) + + # DROPLET SPLITTER + + droplet_splitter_inputs = [] + + droplet_splitter_inputs.append(ConnectingOption(None, ["1"])) + + droplet_splitter_outputs = [] + + droplet_splitter_outputs.append(ConnectingOption(None, ["2"])) + droplet_splitter_outputs.append(ConnectingOption(None, ["3"])) + + droplet_splitter_loadings = [] + droplet_splitter_carriers = [] + + droplet_splitter = Primitive( + "DROPLET SPLITTER", + PrimitiveType.COMPONENT, + "DIVIDE", + False, + False, + droplet_splitter_inputs, + droplet_splitter_outputs, + droplet_splitter_loadings, + droplet_splitter_carriers, + None, + ) + + library.add_operator_entry(droplet_splitter, InteractionType.DIVIDE) + + # DROPLET CAPACITANCE SENSOR + + droplet_capacitance_sensor_inputs = [] + + droplet_capacitance_sensor_inputs.append(ConnectingOption(None, ["1"])) + + droplet_capacitance_sensor_outputs = [] + + droplet_capacitance_sensor_outputs.append(ConnectingOption(None, ["2"])) + + droplet_capacitance_sensor_loadings = [] + droplet_capacitance_sensor_carriers = [] + + droplet_capacitance_sensor = Primitive( + "DROPLET CAPACITANCE SENSOR", + PrimitiveType.COMPONENT, + "PROCESS", + False, + False, + droplet_capacitance_sensor_inputs, + droplet_capacitance_sensor_outputs, + droplet_capacitance_sensor_loadings, + droplet_capacitance_sensor_carriers, + None, + ) + + library.add_operator_entry( + droplet_capacitance_sensor, InteractionType.TECHNOLOGY_PROCESS + ) + + # DROPLET FLUORESCENCE SENSOR + + droplet_fluorescence_sensor_inputs = [] + + droplet_fluorescence_sensor_inputs.append(ConnectingOption(None, ["1"])) + + droplet_fluorescence_sensor_outputs = [] + + droplet_fluorescence_sensor_outputs.append(ConnectingOption(None, ["2"])) + + droplet_fluorescence_sensor_loadings = [] + droplet_fluorescence_sensor_carriers = [] + + droplet_fluorescence_sensor = Primitive( + "DROPLET FLUORESCENCE SENSOR", + PrimitiveType.COMPONENT, + "PROCESS", + False, + False, + droplet_fluorescence_sensor_inputs, + droplet_fluorescence_sensor_outputs, + droplet_fluorescence_sensor_loadings, + droplet_fluorescence_sensor_carriers, + None, + ) + + library.add_operator_entry( + droplet_fluorescence_sensor, InteractionType.TECHNOLOGY_PROCESS + ) + + # DROPLET LUMINESCENCE SENSOR + droplet_luminescence_sensor_inputs = [] + + droplet_luminescence_sensor_inputs.append(ConnectingOption(None, ["1"])) + + droplet_luminescence_sensor_outputs = [] + + droplet_luminescence_sensor_outputs.append(ConnectingOption(None, ["2"])) + + droplet_luminescence_sensor_loadings = [] + droplet_luminescence_sensor_carriers = [] + + droplet_luminescence_sensor = Primitive( + "DROPLET CAPACITANCE SENSOR", + PrimitiveType.COMPONENT, + "PROCESS", + False, + False, + droplet_luminescence_sensor_inputs, + droplet_luminescence_sensor_outputs, + droplet_luminescence_sensor_loadings, + droplet_luminescence_sensor_carriers, + None, + ) + + library.add_operator_entry( + droplet_luminescence_sensor, InteractionType.TECHNOLOGY_PROCESS + ) + + # DROPLET SPACER + + droplet_spacer_inputs = [] + + droplet_spacer_inputs.append(ConnectingOption("default_component", ["1"])) + + droplet_spacer_outputs = [] + + droplet_spacer_outputs.append(ConnectingOption("default_component", ["2"])) + + droplet_spacer_loadings = [] + droplet_spacer_carriers = [] + + droplet_spacer = Primitive( + "DROPLET SPACER", + PrimitiveType.NETLIST, + "PROCESS", + False, + False, + droplet_spacer_inputs, + droplet_spacer_outputs, + droplet_spacer_loadings, + droplet_spacer_carriers, + "default-netlists/dropletspacer.mint", + ) + + library.add_operator_entry(droplet_spacer, InteractionType.TECHNOLOGY_PROCESS) + + # YTREE - This is a procedural primitives + + ytree = YTREE() + + library.add_procedural_entry(ytree) + + return library + + +def generate(module: Module, library: MappingLibrary) -> MINTDevice: + + construction_graph = ConstructionGraph() + + name_generator = NameGenerator() + + cur_device = MINTDevice(module.name) + + # Add a MINT Layer so that the device has something to work with + cur_device.create_mint_layer("0", "0", 0, MINTLayerType.FLOW) + + # TODO - I need to change this DummyStrategy later on + if library.name == "dropx": + active_strategy = DropXStrategy(construction_graph, module.FIG) + elif library.name == "mars": + raise NotImplementedError() + elif library.name == "hmlp": + raise NotImplementedError() + else: + active_strategy = DummyStrategy(construction_graph, module.FIG) + + # First go through all the interactions in the design + + # IF interaction is mix/sieve/divide/dilute/meter look at the library + # to get all the options available and set them up as options for the + # construction graph to pick and choose from the options. + # + # FUTURE WORK + # + # Do the regex matching to find the mapping options + # This means that we might need to have a forest of construction of graphs + # as there would be alternatives for each type of mapping + for interaction in module.FIG.get_interactions(): + operator_candidates = library.get_operators(interaction_type=interaction.type) + cn = ConstructionNode(interaction.id) + # if isinstance(interaction, ValueNode): + # continue + + for operator_candidate in operator_candidates: + # TODO: This will change in the future when we can match subgraphs correctly + if isinstance(interaction, FluidNumberInteraction) or isinstance( + interaction, FluidIntegerInteraction + ): + # Basically add the value node id into the subgraph view also + node_ids = [ + module.FIG.get_fignode(edge[0]).id + for edge in module.FIG.in_edges(interaction.id) + if isinstance(module.FIG.get_fignode(edge[0]), ValueNode) + ] + node_ids.append(interaction.id) + sub_graph = module.FIG.subgraph(node_ids) + else: + sub_graph = module.FIG.subgraph(interaction.id) + mapping_option = MappingOption(operator_candidate, sub_graph) + cn.add_mapping_option(mapping_option) + + construction_graph.add_construction_node(cn) + + # Generate all ports necessary for the Explicitly declared IO + # ------- + # Generate the flow layer IO. These are typically declared explicitly + # TODO - Figure out how we should generate the construction nodes for control networks + + for io_ref in module.io: + if io_ref.type is IOType.CONTROL: + continue + for io in io_ref.vector_ref: + cn = ConstructionNode(io.id) + sub_graph = module.FIG.subgraph(io.id) + mapping_candidate = library.get_default_IO() + mapping_option = MappingOption(mapping_candidate, sub_graph) + cn.add_mapping_option(mapping_option) + + construction_graph.add_construction_node(cn) + + # TODO - Go through the different flow-flow edge networks to generate construction nodes + # specific to these networks, Conditions: + # if its a 1-1 flow-flow connection, then create a construction node for the two flow nodes + # if its a 1-n / n-1 / n-n construction nodes, then create a construction node capturing the whole network + + # TODO - Deal with coverage issues here since we need to figure out what are the flow networks, + # that we want to match first and then ensure that they're no included on any list + cn_nodes = get_flow_flow_candidates(module, active_strategy) + for cn in cn_nodes: + construction_graph.add_construction_node(cn) + + # Apply all the explicit mappings in the module to the nodes, overwriting + # the options from the library to match against + # TODO - Modify Explicit Mapping Data structure + + # Find all the explicit mappings and override them in the construction graph + mappings = module.get_explicit_mappings() + override_mappings(mappings, library, module.FIG, construction_graph) + + # Whittle Down the mapping options here to only include the requried single candidates + # TODO - Check what library is being used and use the required library here + active_strategy.reduce_mapping_options() + + # TODO - Consider what needs to get done for a combinatorial design space + # ---------------- + # Generate edges in the construction graph, these edges will guide the generation/ + # reduction of path and pipelineing that needs to get done for mars devices + construction_graph.generate_edges(module.FIG) + + # TODO - Validate if this is a legit way to do things + override_network_mappings(mappings, library, module.FIG, construction_graph) + + # TODO - Extract all pass through networks + eliminate_passthrough_nodes(construction_graph) + + # Now since all the mapping options are finalized Extract the netlist necessary + construction_graph.construct_components(name_generator, cur_device) + + construction_graph.construct_connections(name_generator, cur_device) + + # Finally join all the netlist pieces attached to the construction nodes + # and the input/output/load/carrier flows + # TODO - MINIMIZE - carrier / load flows - this might require us to generate + # multiple netlist options and pick the best + construction_graph.generate_flow_cn_edges(module) + + construction_graph.generate_control_cn_edges(module) + + # Generate all the unaccounted carriers and waste output lines necessary + # for this to function + connect_orphan_IO() + + # Size the component netlist + active_strategy.size_netlist(cur_device) + + return cur_device + + +def override_mappings( + mappings: List[NodeMappingTemplate], + mapping_library: MappingLibrary, + fig: FluidInteractionGraph, + construction_graph: ConstructionGraph, +) -> None: + # Go through the entire set of mappings in the FIG and generate / append the mapping options + # Step 1 - Loop through each of the mappingtemplates + # Step 2 - Loop through each of the instances in teh mappingtemplate + # Step 3 - Find the cn associated with each of the fig nodes and override the explicit mapping if mappingtemplate has an associated technology string + assign_node_index = 0 + for mapping in mappings: + for instance in mapping.instances: + + primitive_to_use = None + if mapping.technology_string is not None: + # Create a mapping option from the library with the corresponding info + primitive_to_use = mapping_library.get_primitive( + mapping.technology_string + ) + + node_ids = [] + cn = None # Get the right construction node for doing the stuff + cn_mapping_options = [] + + if isinstance(instance, NetworkMapping): + # node_ids.extend(n.id for n in instance.input_nodes) + # node_ids.extend(n.id for n in instance.output_nodes) + # subgraph = fig.subgraph(node_ids) + # try: + # # TODO - Incase this is a flow-flow candidate, we need to get the + # # cn corresponding to this mapping. + # # TODO - do we need to have a new flow node constructed + + # cn = construction_graph.get_subgraph_cn(subgraph) + # except Exception as e: + # # Incase we cannot find a corresponding construction node, + # # we need to create a new construction node + # print(e) + # cn = ConstructionNode("assign_{}".format(assign_node_index)) + # # Increment the index of the assign construction node + # assign_node_index += 1 + + # # Find the cn's associated with the input nodes + # input_cns = [] + # output_cns = [] + # for fig_node in instance.input_nodes: + # cn_temp = construction_graph.get_fignode_cn(fig_node) + # if cn_temp not in input_cns: + # input_cns.append(cn_temp) + # for fig_node in instance.output_nodes: + # cn_temp = construction_graph.get_fignode_cn(fig_node) + # if cn_temp not in output_cns: + # output_cns.append(cn_temp) + + # # Now insert the node + # construction_graph.insert_cn(cn, input_cns, output_cns) + + # mapping_option = NetworkMappingOption( + # network_primitive=primitive_to_use, + # mapping_type=NetworkMappingOptionType.COMPONENT_REPLACEMENT, + # subgraph_view=subgraph, + # ) + # cn.use_explicit_mapping(mapping_option) + # cn_mapping_options.append(mapping_option) + # cn_mapping_options.extend(cn.mapping_options) + print( + "Skipping Network Mapping: \n Input - {} \n Output - {}".format( + ",".join([n.id for n in instance.input_nodes]), + ",".join([n.id for n in instance.output_nodes]), + ) + ) + continue + else: + print("Applying Network Mapping: \n Nodes - {}".format(instance.node)) + + # Find the construction node assicated with the + # FIG node and then do the followinging: + # Step 1 - If the mappingtemplate has no technology string assiciated + # with the mapping, just apply the constraints to the associated mapping + # options + + # Step 2 - In there is a string assiciated with the mappingtemplate, we + # eliminate all mapping options that dont have a matching string / generate + # a mapping option with the corresponding + + # In the case of an Fluid Value interaction put all valuenodes in the subgraph + node_ids.extend( + [ + fig.get_fignode(edge[0]).id + for edge in fig.in_edges(instance.node.id) + if isinstance(fig.get_fignode(edge[0]), ValueNode) + ] + ) + node_ids.append(instance.node.id) + subgraph = fig.subgraph(node_ids) + + # Get the Construction node that has the corresponding subgraph, + # and then replace the mapping option + cn = construction_graph.get_subgraph_cn(subgraph) + if primitive_to_use is not None: + mapping_option = MappingOption(primitive_to_use, subgraph) + cn.use_explicit_mapping(mapping_option) + cn_mapping_options.append(mapping_option) + else: + # Add the constraints to all the mapping options + # This is an example where since no explicit mapping + # was specified, we only add the performance/material + # constraints. This can be ulitized for whittling down + # options later if necessary. + cn_mapping_options.extend(cn.mapping_options) + + # Now that we know what the mapping options are (either explicit + # loaded from the library, we can add the performance constraints) + for mapping_option in cn_mapping_options: + # Add all the constraints to the mapping_option + cn.constraints.extend(mapping.constraints) + + +def override_network_mappings( + mappings: List[NodeMappingTemplate], + mapping_library: MappingLibrary, + fig: FluidInteractionGraph, + construction_graph: ConstructionGraph, +) -> None: + # Go through the entire set of mappings in the FIG and generate / append the mapping options + # Step 1 - Loop through each of the mappingtemplates + # Step 2 - Loop through each of the instances in teh mappingtemplate + # Step 3 - Find the cn associated with each of the fig nodes and override the explicit mapping if mappingtemplate has an associated technology string + assign_node_index = 0 + for mapping in mappings: + for instance in mapping.instances: + + primitive_to_use = None + if mapping.technology_string is not None: + # Create a mapping option from the library with the corresponding info + primitive_to_use = mapping_library.get_primitive( + mapping.technology_string + ) + + node_ids = [] + cn = None # Get the right construction node for doing the stuff + cn_mapping_options = [] + + if isinstance(instance, NetworkMapping): + print( + "Applying Network Mapping: \n Input - {} \n Output - {}".format( + ",".join([n.id for n in instance.input_nodes]), + ",".join([n.id for n in instance.output_nodes]), + ) + ) + + node_ids.extend(n.id for n in instance.input_nodes) + node_ids.extend(n.id for n in instance.output_nodes) + subgraph = fig.subgraph(node_ids) + try: + # TODO - Incase this is a flow-flow candidate, we need to get the + # cn corresponding to this mapping. + # TODO - do we need to have a new flow node constructed + + cn = construction_graph.get_subgraph_cn(subgraph) + except Exception as e: + # Incase we cannot find a corresponding construction node, + # we need to create a new construction node + print(e) + cn = ConstructionNode("assign_{}".format(assign_node_index)) + # Increment the index of the assign construction node + assign_node_index += 1 + + # Find the cn's associated with the input nodes + input_cns = [] + output_cns = [] + for fig_node in instance.input_nodes: + cn_temp = construction_graph.get_fignode_cn(fig_node) + if cn_temp not in input_cns: + input_cns.append(cn_temp) + for fig_node in instance.output_nodes: + cn_temp = construction_graph.get_fignode_cn(fig_node) + if cn_temp not in output_cns: + output_cns.append(cn_temp) + + # Now insert the node + construction_graph.insert_cn(cn, input_cns, output_cns) + + mapping_option = NetworkMappingOption( + network_primitive=primitive_to_use, + mapping_type=NetworkMappingOptionType.COMPONENT_REPLACEMENT, + subgraph_view=subgraph, + ) + cn.use_explicit_mapping(mapping_option) + cn_mapping_options.append(mapping_option) + cn_mapping_options.extend(cn.mapping_options) + else: + # # Find the construction node assicated with the + # # FIG node and then do the followinging: + # # Step 1 - If the mappingtemplate has no technology string assiciated + # # with the mapping, just apply the constraints to the associated mapping + # # options + + # # Step 2 - In there is a string assiciated with the mappingtemplate, we + # # eliminate all mapping options that dont have a matching string / generate + # # a mapping option with the corresponding + + # # In the case of an Fluid Value interaction put all valuenodes in the subgraph + # node_ids.extend( + # [ + # fig.get_fignode(edge[0]).id + # for edge in fig.in_edges(instance.node.id) + # if isinstance(fig.get_fignode(edge[0]), ValueNode) + # ] + # ) + # node_ids.append(instance.node.id) + # subgraph = fig.subgraph(node_ids) + + # # Get the Construction node that has the corresponding subgraph, + # # and then replace the mapping option + # cn = construction_graph.get_subgraph_cn(subgraph) + # if primitive_to_use is not None: + # mapping_option = MappingOption(primitive_to_use, subgraph) + # cn.use_explicit_mapping(mapping_option) + # cn_mapping_options.append(mapping_option) + # else: + # # Add the constraints to all the mapping options + # # This is an example where since no explicit mapping + # # was specified, we only add the performance/material + # # constraints. This can be ulitized for whittling down + # # options later if necessary. + # cn_mapping_options.extend(cn.mapping_options) + continue + # Now that we know what the mapping options are (either explicit + # loaded from the library, we can add the performance constraints) + for mapping_option in cn_mapping_options: + # Add all the constraints to the mapping_option + cn.constraints.extend(mapping.constraints) + + +def eliminate_passthrough_nodes(construction_graph: ConstructionGraph): + for node_id in list(construction_graph.nodes): + cn = construction_graph.get_cn(node_id) + assert len(cn.mapping_options) == 1 + mapping_option = cn.mapping_options[0] + if isinstance(mapping_option, NetworkMappingOption): + if mapping_option.mapping_type is NetworkMappingOptionType.PASS_THROUGH: + + print("Eliminating PASS THROUGH construction node = {}".format(cn.id)) + + # First get all the in and out edges + in_edges = list(construction_graph.in_edges(node_id)) + out_edges = list(construction_graph.out_edges(node_id)) + + # In Points + in_points = [in_edge[0] for in_edge in in_edges] + out_points = [out_edge[1] for out_edge in out_edges] + + # Delete the node + construction_graph.delete_node(node_id) + + # Create edges for the different cases + # Case 1 - 1->1 + if len(in_points) == 1 and len(out_points) == 1: + construction_graph.add_edge(in_points[0], out_points[0]) + # Case 2 - n->1 + # Case 3 - 1->n + elif (len(in_points) > 1 and len(out_points) == 1) or ( + len(in_points) == 1 and len(out_points) > 1 + ): + for in_point in in_points: + for out_point in out_points: + construction_graph.add_edge(in_point, out_point) + else: + raise Exception( + "Pass through network node elimination not implemented \ + when n->n edge creation is necessary" + ) + + +def connect_orphan_IO(): + print("Implement the orphan io generation system") + + +def get_flow_flow_candidates( + module: Module, gen_strategy: GenStrategy +) -> List[ConstructionNode]: + # TODO - go through all the edges and see which ones are between flow-flow graphs + # If these connectsions are between flow-flow nodes then we need to figure out + # which ones are part of the same network/connected graphs with only flow nodes + # The networks with only the flow nodes will need to be covered as a part of. + # these construction nodes. + + ret = [] + + # Step 1. Do a shallow copy of the graph + # Step 2. Remove all the fignodes that are not Flow + # Step 3. Now get the all the disconnected pieces of the graph + # Step 4. Create a Construction node for each of the disconnected pieces + # Return all the constructions nodes + + # Step 1. Do a shallow copy of the graph + fig_original = module.FIG + fig_copy = ( + module.FIG.copy() + ) # Note this does not copy anything besides the nx.DiGraph at the moment + + # Step 2. Remove all the fignodes that are not Flow + remove_list = [] + for node_id in fig_copy.nodes: + node = fig_original.get_fignode(node_id) + if node.match_string != "FLOW": + remove_list.append(node_id) + + for node_id in remove_list: + fig_copy.remove_node(node_id) + + # Step 3. Now get the all the disconnected pieces of the graph + i = 0 + for component in nx.connected_components(fig_copy.to_undirected()): + print("Flow candidate") + print(component) + sub = fig_original.subgraph(component) + # TODO - Decide what the mapping type should be. for now assume that we just a single + # passthrough type scenario where we don't have to do much work + is_passthrough = __check_if_passthrough(sub) + if is_passthrough: + mapping_type = NetworkMappingOptionType.PASS_THROUGH + else: + mapping_type = NetworkMappingOptionType.CHANNEL_NETWORK + nprimitive = NetworkPrimitive(sub, gen_strategy) + nprimitive.generate_netlist() + mapping_option = NetworkMappingOption(nprimitive, mapping_type, sub) + # Step 4. Create a Construction node for each of the disconnected pieces + cn = ConstructionNode("flow_network_{}".format(i)) + cn.add_mapping_option(mapping_option) + + i += 1 + ret.append(cn) + + return ret + + +def size_netlist(): + # Size all the node's netlist components to based on the CONSTRAINTS set + # by the postprocessor + # TODO - Modify datastructure in library and other places + netlist_user_constriants = module.get_user_constriants() + + construction_graph.fix_component_params(netlist_user_constriants) + + # Size all the Meter/Dilute/Divide nodes based on the value nodes + # TODO - Talk to Ali about this for strategy + construction_graph.size_components() + + +def __check_if_passthrough(sub) -> bool: + # Return true if its a single chain of flow channels + in_count = 0 + out_count = 0 + for node in list(sub.nodes): + inedges = list(sub.in_edges(node)) + outedges = list(sub.out_edges(node)) + if len(inedges) == 0: + in_count += 1 + if len(outedges) == 0: + out_count += 1 + + if in_count == 1 and out_count == 1: + return True + else: + return False diff --git a/lfr/graphmatch/__init__.py b/lfr/graphmatch/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/lfr/graphmatch/figmappingmatcher.py b/lfr/graphmatch/figmappingmatcher.py new file mode 100644 index 0000000..dc62762 --- /dev/null +++ b/lfr/graphmatch/figmappingmatcher.py @@ -0,0 +1,60 @@ +from typing import Dict +from networkx.algorithms.isomorphism import DiGraphMatcher +from networkx.classes import digraph +from reggie.nodefilter import NodeFilter +from lfr.fig.fluidinteractiongraph import FluidInteractionGraph + + +class FIGMappingMatcher(DiGraphMatcher): + """Implementation of VF2 algorithm for matching undirected graphs. + + Suitable for Graph and MultiGraph instances. + """ + + def __init__( + self, + G1: FluidInteractionGraph, + G2: digraph.DiGraph, + semantic_information: Dict[str, NodeFilter], + ): + self._semantic_information = semantic_information + super(FIGMappingMatcher, self).__init__(G1, G2) + + def semantic_feasibility(self, G1_node, G2_node): + """Returns True if adding (G1_node, G2_node) is symantically feasible. + + The semantic feasibility function should return True if it is + acceptable to add the candidate pair (G1_node, G2_node) to the current + partial isomorphism mapping. The logic should focus on semantic + information contained in the edge data or a formalized node class. + + By acceptable, we mean that the subsequent mapping can still become a + complete isomorphism mapping. Thus, if adding the candidate pair + definitely makes it so that the subsequent mapping cannot become a + complete isomorphism mapping, then this function must return False. + + The default semantic feasibility function always returns True. The + effect is that semantics are not considered in the matching of G1 + and G2. + + The semantic checks might differ based on the what type of test is + being performed. A keyword description of the test is stored in + self.test. Here is a quick description of the currently implemented + tests:: + + test='graph' + Indicates that the graph matcher is looking for a graph-graph + isomorphism. + + test='subgraph' + Indicates that the graph matcher is looking for a subgraph-graph + isomorphism such that a subgraph of G1 is isomorphic to G2. + + Any subclass which redefines semantic_feasibility() must maintain + the above form to keep the match() method functional. Implementations + should consider multigraphs. + """ + + # TODO - Get the semantic information from here and then use it to figure out if its right type of node or not + raise NotImplementedError() + return True diff --git a/lfr/graphmatch/interface.py b/lfr/graphmatch/interface.py new file mode 100644 index 0000000..9d32313 --- /dev/null +++ b/lfr/graphmatch/interface.py @@ -0,0 +1,50 @@ +from networkx.classes.function import restricted_view +from lfr.graphmatch.figmappingmatcher import FIGMappingMatcher +from typing import Dict +from lfr.netlistgenerator.mappinglibrary import MappingLibrary +from lfr.fig.fluidinteractiongraph import FluidInteractionGraph +from reggie.matchpattern import MatchPattern + + +def match_node_constraints(semantic_information, subgraph, mapping) -> bool: + # TODO - Check if the constraints match for the subgraph + # Loop through each of the unique constraints in the overall semantic information + # Check if the 1) Constraint type matches, 2) if the same nodes in the GM.mapping + # match the ones denoted by the subgraph LFR + raise NotImplementedError() + + +def extract_subgraph_mapping(subgraph, mapping) -> Dict[str, str]: + ret = dict() + raise NotImplementedError() + return ret + + +def match(fig: FluidInteractionGraph, library: MappingLibrary): + patterns: Dict[ + str, object + ] = dict() # Store the mint and the match pattern object here + + ret = [] + + # TODO - Retrun the networkx subgraph views of the of the FIG + # Step 1 - Generate the match candidates by running the subgraph isomerism for all the items stored in the library + for (mint, match_pattern_string) in library.get_match_patterns(): + pattern = MatchPattern(match_pattern_string) + patterns[mint] = pattern + structural_template = pattern.get_structural_template() + semantic_information = pattern.get_semantic_template() + GM = FIGMappingMatcher(fig, structural_template, semantic_information) + for subgraph in GM.subgraph_isomorphisms_iter(): + print(subgraph) + # TODO - Not sure what, but work with these subgraphs at the end and then push them through the constraint checking phase + # This would be a match, figure out how to get the mapping from GM.mapping + + # Loop through each of the candates + # TODO - Compare the constraints on all the nodes for this for subgraph to confirm the match + if match_node_constraints(semantic_information, subgraph, GM.mapping): + # TODO - Extract the specific mapping for the subgraph + subgraph_mapping = extract_subgraph_mapping(subgraph, GM.mapping) + ret.append((subgraph, subgraph_mapping)) + + return ret diff --git a/lfr/netlistgenerator/mappinglibrary.py b/lfr/netlistgenerator/mappinglibrary.py index 5186d7a..632efb2 100644 --- a/lfr/netlistgenerator/mappinglibrary.py +++ b/lfr/netlistgenerator/mappinglibrary.py @@ -1,5 +1,5 @@ -from typing import Dict, List - +from lfr.netlistgenerator.primitive import Primitive, ProceduralPrimitive +from typing import Dict, List, Tuple from lfr.fig.interaction import InteractionType from lfr.netlistgenerator.primitive import Primitive, ProceduralPrimitive @@ -93,3 +93,10 @@ def get_primitive(self, technology_string: str) -> Primitive: def has_primitive(self, technology_string: str) -> bool: return technology_string in self.__all_primitives.keys() + + def get_match_patterns(self) -> List[Tuple[str, str]]: + ret = [] + for primitive in self.__all_primitives.values(): + ret.append((primitive.mint, primitive.match_string)) + + return ret diff --git a/lfr/netlistgenerator/primitive.py b/lfr/netlistgenerator/primitive.py index 42c39ae..41daa01 100644 --- a/lfr/netlistgenerator/primitive.py +++ b/lfr/netlistgenerator/primitive.py @@ -79,6 +79,10 @@ def type(self) -> PrimitiveType: def mint(self) -> str: return self._mint + @property + def match_string(self) -> str: + return self._match_string + def export_inputs(self, subgraph) -> List[ConnectingOption]: return [copy.copy(c) for c in self._inputs] diff --git a/notes.md b/notes.md new file mode 100644 index 0000000..a2566d3 --- /dev/null +++ b/notes.md @@ -0,0 +1,259 @@ +### MIXER + +``` +{ + v1:MIX +} +``` + +### DROPLET CAPACITANCE SENSOR + +``` +{ + v1:TECHNOLOGY +} +``` + +### LONG CELL TRAP + +``` +{ + v1:STORAGE +} +``` + +### SQUARE CELL TRAP + +``` +{ + v1:STORAGE +} +``` + +### REACTION CHAMBER + +``` +{ + v1:STROAGE +} +``` + +### CHEMOSTAT RING + +``` +{ + v1:STORAGE +} +``` + +### CURVED MIXER + +``` +{ + v1:MIX +} +``` + +### DIAMOND REACTION CHAMBER + +``` +{ + v1:STORAGE +} +``` + +### NOZZLE DROPLET GENERATOR + +``` +{ + v1:METER +} +``` + +### DROPLET GENERATOR FLOW FOCUS + +``` +{ + v1:IO -> v2:METER +} +``` + +### DROPLET GENERATOR T + +``` +{ + v1:METER +} +``` + +### DROPLET MERGER + +``` +{ + v1:MIX +} +``` + +### FILTER + +``` +{ + v1:TECHNOLOGY +} +``` + +### GRADIENT GENERATOR + +TODO - Figure out how to show this as the gradient generator +``` +{ + ??????? +} +``` + +### LL CHAMBER +TODO - Change the name of this + +``` +{ + v1:MIX -> v2:STORAGE +} +``` + +### LOGIC ARRAY + +TODO - Figure out how to control sequences work from an LFR file. Also figure out where the + +``` +{ + v1:STORAGE <-> v2:STORAGE, + v1:STORAGE <-> v3:STORAGE, + v1:STORAGE <-> v4:STORAGE, + v2:STORAGE <-> v3:STORAGE, + v3:STORAGE <-> v4:STORAGE +} +``` + +### DROPLET MERGER + +``` +{ + v1:MIX +} +``` + +### MUX +``` +{ + ?:DISTRIBUTE_OR { "or_1" } -> v1, + (?:DISTRIBUTE_OR { "or_1" } -> v1)+ +} +``` + +``` +{ + v1 -> ?:DISTRIBUTE_OR { "or_1" }, + (v1 -> ?:DISTRIBUTE_OR { "or_1" })+ +} +``` + +### PICOINJECTOR +``` +{ + v1:MIX +} +``` + +### PORT + +``` +{ + v1:IO +} +``` + +### PUMP + +``` +{ + v1:PUMP +} +``` + +### PUMP3D + +``` +{ + v1:PUMP +} +``` + +### ROTARY MIXER + +``` +{ + v1:MIX -> v2:STROAGE +} +``` + +``` +{ + v1:STORAGE -> v2:MIX +} +``` + +### DROPLET SORTER + +``` +{ + v1:SIEVE +} +``` + +### MIXER3D + +``` +{ + v1:MIX +} +``` + +### TRANSPOSER + +``` +{ + +} +``` + +### TREE + +``` +{ + v1:FLOW -> ?:FLOW, + (v1:FLOW -> ?:FLOW)+ +} +``` + +``` +{ + ?:FLOW -> v1:FLOW, + (?: FLOW -> v1:FLOW)+ +} +``` + +### YTREE + +``` +{ + v1:FLOW -> ?:FLOW, + (v1:FLOW -> ?:FLOW)+ +} +``` + +``` +{ + ?:FLOW -> v1:FLOW, + (?: FLOW -> v1:FLOW)+ +} +``` \ No newline at end of file From 7b58df08db39b3528c0cc644fa173fd150ccbe9f Mon Sep 17 00:00:00 2001 From: Radhakrishna Sanka Date: Thu, 4 Mar 2021 19:51:28 -0500 Subject: [PATCH 03/36] Modified the whole distribute-annotation thing --- lfr/compiler/distribute/statetable.py | 13 +++-- lfr/fig/annotation.py | 51 +++++++++++++++++++ lfr/fig/fignode.py | 32 ------------ lfr/fig/fluidinteractiongraph.py | 72 +++++++++++++++------------ 4 files changed, 96 insertions(+), 72 deletions(-) create mode 100644 lfr/fig/annotation.py diff --git a/lfr/compiler/distribute/statetable.py b/lfr/compiler/distribute/statetable.py index 7ce35d7..de5e9b3 100644 --- a/lfr/compiler/distribute/statetable.py +++ b/lfr/compiler/distribute/statetable.py @@ -1,5 +1,6 @@ from __future__ import annotations - +from lfr.fig.annotation import ANDAnnotation, NOTAnnotation, ORAnnotation +from lfr.fig.fluidinteractiongraph import FluidInteractionGraph from typing import Dict, List, Tuple import networkx as nx @@ -62,17 +63,15 @@ def save_connectivity( # Add source and target if they are not present if source not in list(digraph.nodes): print( - "Could not find source - {} in state table connectivity graph, adding node".format( - source - ) + "Could not find source - {} in state table connectivity graph, adding" + " node".format(source) ) digraph.add_node(source) if target not in list(digraph.nodes): print( - "Could not find target - {} in state table connectivity graph, adding node".format( - target - ) + "Could not find target - {} in state table connectivity graph, adding" + " node".format(target) ) digraph.add_node(target) diff --git a/lfr/fig/annotation.py b/lfr/fig/annotation.py new file mode 100644 index 0000000..0ac5160 --- /dev/null +++ b/lfr/fig/annotation.py @@ -0,0 +1,51 @@ +from typing import List +from lfr.fig.fignode import FIGNode + + +class DistributeAnnotation: + def __init__(self, id: str) -> None: + self._id = id + self._fignodes = [] + + @property + def id(self) -> str: + return self._id + + def add_fignode(self, fignode: FIGNode) -> None: + self._fignodes.append(fignode) + + def remove_fignode(self, fignode: FIGNode) -> None: + self._fignodes.remove(fignode) + + def get_annotations(self) -> List[FIGNode]: + return self._fignodes + + def clear_annotations(self) -> None: + self._fignodes.clear() + + +class ANDAnnotation(DistributeAnnotation): + def __init__(self, id: str) -> None: + super(ANDAnnotation, self).__init__(id) + + @property + def match_string(self): + return "DISTRIBUTE-AND" + + +class ORAnnotation(DistributeAnnotation): + def __init__(self, id: str) -> None: + super(ORAnnotation, self).__init__(id) + + @property + def match_string(self): + return "DISTRIBUTE-OR" + + +class NOTAnnotation(DistributeAnnotation): + def __init__(self, id: str) -> None: + super(NOTAnnotation, self).__init__(id) + + @property + def match_string(self): + return "DISTRIBUTE-NOT" diff --git a/lfr/fig/fignode.py b/lfr/fig/fignode.py index d24d134..ae56ede 100644 --- a/lfr/fig/fignode.py +++ b/lfr/fig/fignode.py @@ -114,35 +114,3 @@ def __init__(self, id: str) -> None: @property def match_string(self): return "SIGNAL" - - -class DistributeNode(FIGNode): - def __init__(self, id: str) -> None: - super(DistributeNode, self).__init__(id) - - -class ANDAnnotation(DistributeNode): - def __init__(self, id: str) -> None: - super(ANDAnnotation, self).__init__(id) - - @property - def match_string(self): - return "DISTRIBUTE-AND" - - -class ORAnnotation(DistributeNode): - def __init__(self, id: str) -> None: - super(ORAnnotation, self).__init__(id) - - @property - def match_string(self): - return "DISTRIBUTE-OR" - - -class NOTAnnotation(DistributeNode): - def __init__(self, id: str) -> None: - super(NOTAnnotation, self).__init__(id) - - @property - def match_string(self): - return "DISTRIBUTE-NOT" diff --git a/lfr/fig/fluidinteractiongraph.py b/lfr/fig/fluidinteractiongraph.py index c5c4364..670c102 100644 --- a/lfr/fig/fluidinteractiongraph.py +++ b/lfr/fig/fluidinteractiongraph.py @@ -6,14 +6,19 @@ import networkx as nx from lfr.fig.fignode import ( - ANDAnnotation, FIGNode, IONode, IOType, + ValueNode, +) + +from lfr.fig.annotation import ( + ANDAnnotation, + DistributeAnnotation, NOTAnnotation, ORAnnotation, - ValueNode, ) + from lfr.fig.interaction import ( FluidFluidInteraction, FluidIntegerInteraction, @@ -22,7 +27,8 @@ Interaction, InteractionType, ) -from lfr.postprocessor.mapping import NodeMappingTemplate + +import uuid class FluidInteractionGraph(nx.DiGraph): @@ -31,6 +37,7 @@ def __init__(self, data=None, val=None, **attr) -> None: self._fignodes: Dict[str, FIGNode] = {} # self._fluid_interactions = dict() self._gen_id = 0 + self._annotations: Dict[FIGNode, List[DistributeAnnotation]] = dict() def add_fignode(self, node: FIGNode) -> None: self._fignodes[node.id] = node @@ -41,9 +48,8 @@ def get_fignode(self, id: str) -> FIGNode: return self._fignodes[id] else: raise Exception( - "Cannot find the node '{}' in the FluidInteractionGraph".format( - id - ) + "Cannot find the node '{}' in the FluidInteractionGraph" + .format(id) ) def load_fignodes(self, fig_nodes: List[FIGNode]) -> None: @@ -125,39 +131,39 @@ def get_io(self) -> List[IONode]: def add_and_annotation(self, nodes: List[FIGNode]) -> ANDAnnotation: print("Need to implement the generation of the AND annotations") - fig_node_name = "DIST_AND_" + "_".join([node.id for node in nodes]) - annotation_node = ANDAnnotation(fig_node_name) - self.add_fignode(annotation_node) - for node in nodes: - self.add_edge(annotation_node.id, node.id) - return annotation_node + fig_node_name = "DIST_AND_" + str(uuid.uuid4()) + annotation = ANDAnnotation(fig_node_name) + for fignode in nodes: + annotation.add_fignode(fignode) + if fignode in self._annotations.keys(): + self._annotations[fignode].append(annotation) + else: + self._annotations[fignode] = [annotation] + return annotation def add_or_annotation(self, nodes: List[FIGNode]) -> ORAnnotation: print("Need to implement the generation of the OR annotation") - fig_node_name = "DIST_OR_" + "_".join([node.id for node in nodes]) - annotation_node = ORAnnotation(fig_node_name) - self.add_fignode(annotation_node) - for node in nodes: - self.add_edge(annotation_node.id, node.id) - return annotation_node + fig_node_name = "DIST_OR_" + str(uuid.uuid4()) + annotation = ORAnnotation(fig_node_name) + for fignode in nodes: + annotation.add_fignode(fignode) + if fignode in self._annotations.keys(): + self._annotations[fignode].append(annotation) + else: + self._annotations[fignode] = [annotation] + return annotation def add_not_annotation(self, nodes: List[FIGNode]) -> NOTAnnotation: print("Need to implement the generation of the NOT annotation") - fig_node_name = "DIST_NOT_" + "_".join([node.id for node in nodes]) - annotation_node = NOTAnnotation(fig_node_name) - self.add_fignode(annotation_node) - for node in nodes: - self.add_edge(annotation_node.id, node.id) - return annotation_node - - # def generate_match_string(self) -> str: - # # Generate match string that we can use against any kind of a string match system - # ret = '' - # # Start with the inputs - # for ionode in self.get_io(): - # ret += ionode.match_string - - # return ret + fig_node_name = "DIST_NOT_" + str(uuid.uuid4()) + annotation = NOTAnnotation(fig_node_name) + for fignode in nodes: + annotation.add_fignode(fignode) + if fignode in self._annotations.keys(): + self._annotations[fignode].append(annotation) + else: + self._annotations[fignode] = [annotation] + return annotation def add_fig(self, fig_to_add: FluidInteractionGraph) -> None: # Check if any of the incoming fig nodes From 52c507ef327968e51058e62718da03d1845260f5 Mon Sep 17 00:00:00 2001 From: Radhakrishna Sanka Date: Thu, 4 Mar 2021 21:59:26 -0500 Subject: [PATCH 04/36] added hash methods for fignode and annotations --- lfr/fig/annotation.py | 3 +++ lfr/fig/fignode.py | 3 +++ 2 files changed, 6 insertions(+) diff --git a/lfr/fig/annotation.py b/lfr/fig/annotation.py index 0ac5160..4b7300a 100644 --- a/lfr/fig/annotation.py +++ b/lfr/fig/annotation.py @@ -23,6 +23,9 @@ def get_annotations(self) -> List[FIGNode]: def clear_annotations(self) -> None: self._fignodes.clear() + def __hash__(self) -> int: + return hash(self._id) + class ANDAnnotation(DistributeAnnotation): def __init__(self, id: str) -> None: diff --git a/lfr/fig/fignode.py b/lfr/fig/fignode.py index ae56ede..1a7a24e 100644 --- a/lfr/fig/fignode.py +++ b/lfr/fig/fignode.py @@ -41,6 +41,9 @@ def __eq__(self, other): def rename(self, id: str) -> None: self._id = id + def __hash__(self) -> int: + return hash(self._id) + class ValueNode(FIGNode): def __init__(self, id: str, val: float) -> None: From 27c890931756978f466cf8523887eaf79e339e21 Mon Sep 17 00:00:00 2001 From: Radhakrishna Sanka Date: Thu, 4 Mar 2021 21:59:59 -0500 Subject: [PATCH 05/36] modified the get constraints interface for the fluid interaction graph --- lfr/fig/fluidinteractiongraph.py | 35 ++++++++++++++++++++++---------- 1 file changed, 24 insertions(+), 11 deletions(-) diff --git a/lfr/fig/fluidinteractiongraph.py b/lfr/fig/fluidinteractiongraph.py index 670c102..23174cd 100644 --- a/lfr/fig/fluidinteractiongraph.py +++ b/lfr/fig/fluidinteractiongraph.py @@ -37,7 +37,14 @@ def __init__(self, data=None, val=None, **attr) -> None: self._fignodes: Dict[str, FIGNode] = {} # self._fluid_interactions = dict() self._gen_id = 0 - self._annotations: Dict[FIGNode, List[DistributeAnnotation]] = dict() + self._annotations_reverse_map: Dict[ + FIGNode, List[DistributeAnnotation] + ] = dict() + self._annotations: List[DistributeAnnotation] = [] + + @property + def annotations(self) -> List[DistributeAnnotation]: + return self._annotations def add_fignode(self, node: FIGNode) -> None: self._fignodes[node.id] = node @@ -120,7 +127,7 @@ def get_interactions(self) -> List[Interaction]: ] @property - def get_io(self) -> List[IONode]: + def io(self) -> List[IONode]: ret = [] for key in self._fignodes: node = self._fignodes[key] @@ -129,40 +136,46 @@ def get_io(self) -> List[IONode]: return ret + def get_annotation(self, fig_node: FIGNode) -> List[DistributeAnnotation]: + return self._annotations_reverse_map[fig_node] + def add_and_annotation(self, nodes: List[FIGNode]) -> ANDAnnotation: print("Need to implement the generation of the AND annotations") fig_node_name = "DIST_AND_" + str(uuid.uuid4()) annotation = ANDAnnotation(fig_node_name) + self._annotations.append(annotation) for fignode in nodes: annotation.add_fignode(fignode) - if fignode in self._annotations.keys(): - self._annotations[fignode].append(annotation) + if fignode in self._annotations_reverse_map.keys(): + self._annotations_reverse_map[fignode].append(annotation) else: - self._annotations[fignode] = [annotation] + self._annotations_reverse_map[fignode] = [annotation] return annotation def add_or_annotation(self, nodes: List[FIGNode]) -> ORAnnotation: print("Need to implement the generation of the OR annotation") fig_node_name = "DIST_OR_" + str(uuid.uuid4()) annotation = ORAnnotation(fig_node_name) + self._annotations.append(annotation) for fignode in nodes: annotation.add_fignode(fignode) - if fignode in self._annotations.keys(): - self._annotations[fignode].append(annotation) + if fignode in self._annotations_reverse_map.keys(): + self._annotations_reverse_map[fignode].append(annotation) else: - self._annotations[fignode] = [annotation] + self._annotations_reverse_map[fignode] = [annotation] return annotation def add_not_annotation(self, nodes: List[FIGNode]) -> NOTAnnotation: print("Need to implement the generation of the NOT annotation") fig_node_name = "DIST_NOT_" + str(uuid.uuid4()) annotation = NOTAnnotation(fig_node_name) + self._annotations.append(annotation) for fignode in nodes: annotation.add_fignode(fignode) - if fignode in self._annotations.keys(): - self._annotations[fignode].append(annotation) + if fignode in self._annotations_reverse_map.keys(): + self._annotations_reverse_map[fignode].append(annotation) else: - self._annotations[fignode] = [annotation] + self._annotations_reverse_map[fignode] = [annotation] return annotation def add_fig(self, fig_to_add: FluidInteractionGraph) -> None: From 597527429169df0f133b0a6b350ee2a155d08831 Mon Sep 17 00:00:00 2001 From: Radhakrishna Sanka Date: Tue, 6 Apr 2021 17:13:39 -0400 Subject: [PATCH 06/36] Updated State Table to have a control matrix and print out the fully joined matrix --- lfr/compiler/distribute/statetable.py | 52 ++++++++++++++++++++++----- 1 file changed, 43 insertions(+), 9 deletions(-) diff --git a/lfr/compiler/distribute/statetable.py b/lfr/compiler/distribute/statetable.py index de5e9b3..3d83a95 100644 --- a/lfr/compiler/distribute/statetable.py +++ b/lfr/compiler/distribute/statetable.py @@ -1,4 +1,5 @@ from __future__ import annotations +from lfr.utils import convert_list_to_str from lfr.fig.annotation import ANDAnnotation, NOTAnnotation, ORAnnotation from lfr.fig.fluidinteractiongraph import FluidInteractionGraph from typing import Dict, List, Tuple @@ -20,8 +21,9 @@ def __init__(self, signal_list: List[str]) -> None: # state table into flow annotations using z3 self._connectivity_states: Dict[BitVector, nx.DiGraph] = {} # self._colored_graph: nx.DiGraph = None - self._connectivity_matrix = None - self._connectivity_column_headers = None + self._connectivity_matrix = np.zeros((1, 1)) + self._control_matrix = np.zeros((1, 1)) + self._connectivity_column_headers: List[str] = [] self._connectivity_edges = {} self._and_annotations: List[ANDAnnotation] = [] self._or_annotations: List[ORAnnotation] = [] @@ -104,15 +106,20 @@ def generate_connectivity_table(self) -> None: self._connectivity_edges[edge_name] = edge self._connectivity_matrix = np.zeros((row_size, column_size), dtype=int) + self._control_matrix = np.zeros((row_size, len(self._headers)), dtype=int) + # Actually fill out the matrices now i = 0 for state in self._connectivity_states: graph = self._connectivity_states[state] for edge in list(graph.edges): self.__update_connectivity_matix(edge, i, 1) + self.__update_control_matrix(i, state) i += 1 + # TODO - Generate the full connectivity table with mapping options + def generate_and_annotations(self, fig: FluidInteractionGraph) -> None: m = self._connectivity_matrix shape = m.shape @@ -179,7 +186,11 @@ def generate_and_annotations(self, fig: FluidInteractionGraph) -> None: fig.connect_fignodes(source_node, target_node) origin_nodes = [fig.get_fignode(edge[0]) for edge in candidate] - print("Added AND annotation on FIG: {}".format(str(origin_nodes))) + print( + "Added AND annotation on FIG: {}".format( + convert_list_to_str(origin_nodes) + ) + ) assert origin_nodes is not None annotation = fig.add_and_annotation(origin_nodes) self._and_annotations.append(annotation) @@ -220,7 +231,7 @@ def generate_or_annotations(self, fig: FluidInteractionGraph) -> None: # TODO - Go through the rows, see if the row-i and row-j # compute the XOR of the current vector with the accumulate xord_vector = np.logical_xor(accumulate_vector, row_j) - distance = self.__hamming_distance(xord_vector, accumulate_vector) + distance = self.hamming_distance(xord_vector, accumulate_vector) count = self.__ones_count(xord_vector) if distance == 1 and count == ones_count + 1: @@ -273,16 +284,17 @@ def generate_or_annotations(self, fig: FluidInteractionGraph) -> None: found_flag = False annotation_to_use = None for annotation in self._and_annotations: - if source in fig.neighbors(annotation.id): + if source in annotation.get_items(): found_flag = True - annotation_to_use = fig.get_fignode(annotation.id) + annotation_to_use = annotation break if found_flag is True: if annotation_to_use not in args_for_annotation: args_for_annotation.append(annotation_to_use) else: - if source not in args_for_annotation: - args_for_annotation.append(fig.get_fignode(source)) + source_fignode = fig.get_fignode(source) + if source_fignode not in args_for_annotation: + args_for_annotation.append(source_fignode) self._or_annotations.append(fig.add_or_annotation(args_for_annotation)) @@ -306,8 +318,12 @@ def generate_not_annotations(self, fig: FluidInteractionGraph) -> None: annotation = fig.add_not_annotation([source_node, target_node]) self._not_annotations.append(annotation) + def compute_control_mapping(self) -> None: + print("TODO - Implement method to generate the control mapping") + # TODO - Generate the full connectivity table with mapping options + @staticmethod - def __hamming_distance(vec1, vec2) -> int: + def hamming_distance(vec1, vec2) -> int: assert vec1.size == vec2.size # Start with a distance of zero, and count up distance = 0 @@ -339,6 +355,11 @@ def __update_connectivity_matix(self, edge, row, value): column = self._connectivity_column_headers.index(edge_name) m[row, column] = value + def __update_control_matrix(self, row_index: int, control_state_vector: BitVector): + m = self._control_matrix + for column_index in range(control_state_vector.length()): + m[row_index, column_index] = control_state_vector[column_index] + def add_to_column_skip_list(self, edge: Tuple[str, str]): # TODO - add the column to skip edge list to # prevent double count during xor finding @@ -351,4 +372,17 @@ def print_connectivity_table(self): m = self._connectivity_matrix headers = self._connectivity_column_headers table = tabulate(m, headers, tablefmt="fancy_grid") + + m2 = self._control_matrix + control_headers = self._headers + table2 = tabulate(m2, control_headers, tablefmt="fancy_grid") + print(table) + print(table2) + + m3 = np.concatenate((m, m2), axis=1) + headers_full = headers.copy() + headers_full.extend(control_headers) + table3 = tabulate(m3, headers_full, tablefmt="fancy_grid") + + print(table3) From 8b77a78c8a63538bb22b3fb896fb10966397cfd5 Mon Sep 17 00:00:00 2001 From: Radhakrishna Sanka Date: Tue, 6 Apr 2021 17:50:16 -0400 Subject: [PATCH 07/36] Everything seems to be working without too much of a hassle. #TODO - will need to verify if multi level annotations are getting screwed up here --- lfr/compiler/distribute/distributeblock.py | 8 +- lfr/compiler/module.py | 7 + lfr/fig/annotation.py | 26 ++-- lfr/fig/fignode.py | 5 +- lfr/fig/fluidinteractiongraph.py | 136 +++++++++++++++----- lfr/graphmatch/figmappingmatcher.py | 14 +- lfr/graphmatch/interface.py | 57 +++++--- lfr/netlistgenerator/v2/constructionnode.py | 1 + lfr/netlistgenerator/v2/generator.py | 127 ++++++++++++------ lfr/utils.py | 19 ++- scripts/all.sh | 104 +++++++++++++++ 11 files changed, 396 insertions(+), 108 deletions(-) create mode 100755 scripts/all.sh diff --git a/lfr/compiler/distribute/distributeblock.py b/lfr/compiler/distribute/distributeblock.py index 1054ec4..dac0a1d 100644 --- a/lfr/compiler/distribute/distributeblock.py +++ b/lfr/compiler/distribute/distributeblock.py @@ -17,6 +17,10 @@ def generate_fig(self, fig: FluidInteractionGraph) -> None: print("Implement the fig generation from this") self._state_table.generate_connectivity_table() + # Save a copy of this on the fig + # TODO - Check if we need to make a new export item out of this or not + fig.add_state_table(self._state_table) + print("Connectivity table for the distribution block") self._state_table.print_connectivity_table() @@ -24,12 +28,12 @@ def generate_fig(self, fig: FluidInteractionGraph) -> None: self._state_table.generate_or_annotations(fig) - # TODO - Mark all the single items with no pairs + # Mark all the single items with no pairs self._state_table.generate_not_annotations(fig) # TODO - How to map the control mappings for each # of the annotations to the control signals - # self._state_table.compute_control_mapping() + self._state_table.compute_control_mapping() @property def state_table(self) -> StateTable: diff --git a/lfr/compiler/module.py b/lfr/compiler/module.py index 87ac4d3..5d4e89d 100644 --- a/lfr/compiler/module.py +++ b/lfr/compiler/module.py @@ -212,7 +212,14 @@ def instantiate_module( for node in list(fig_copy.nodes): rename_map[node] = self.__generate_instance_node_name(node, var_name) + # Step 4.1 - Relabel all the annotations with the prefix defined by var_name + for annotation in list(fig_copy.annotations): + rename_map[annotation.id] = self.__generate_instance_node_name( + annotation.id, var_name + ) + fig_copy.rename_nodes(rename_map) + fig_copy.rename_annotations(rename_map) # Step 5 - Stitch together tall the io newly formed io nodes into # current fig diff --git a/lfr/fig/annotation.py b/lfr/fig/annotation.py index 4b7300a..ed37436 100644 --- a/lfr/fig/annotation.py +++ b/lfr/fig/annotation.py @@ -1,30 +1,34 @@ -from typing import List +from __future__ import annotations +from typing import List, Union from lfr.fig.fignode import FIGNode class DistributeAnnotation: def __init__(self, id: str) -> None: self._id = id - self._fignodes = [] + self._annotated_items: List[Union[FIGNode, DistributeAnnotation]] = [] @property def id(self) -> str: return self._id - def add_fignode(self, fignode: FIGNode) -> None: - self._fignodes.append(fignode) + def rename(self, new_id: str) -> None: + self._id = new_id - def remove_fignode(self, fignode: FIGNode) -> None: - self._fignodes.remove(fignode) + def add_annotated_item(self, item: Union[FIGNode, DistributeAnnotation]) -> None: + self._annotated_items.append(item) - def get_annotations(self) -> List[FIGNode]: - return self._fignodes + def remove_item(self, item: Union[FIGNode, DistributeAnnotation]) -> None: + self._annotated_items.remove(item) - def clear_annotations(self) -> None: - self._fignodes.clear() + def get_items(self) -> List[Union[FIGNode, DistributeAnnotation]]: + return self._annotated_items + + def clear_fignodes(self) -> None: + self._annotated_items.clear() def __hash__(self) -> int: - return hash(self._id) + return hash(hex(id(self))) class ANDAnnotation(DistributeAnnotation): diff --git a/lfr/fig/fignode.py b/lfr/fig/fignode.py index 1a7a24e..9912107 100644 --- a/lfr/fig/fignode.py +++ b/lfr/fig/fignode.py @@ -42,7 +42,7 @@ def rename(self, id: str) -> None: self._id = id def __hash__(self) -> int: - return hash(self._id) + return hash(hex(id(self))) class ValueNode(FIGNode): @@ -58,6 +58,9 @@ def value(self): def match_string(self): return "VALUE" + def __str__(self) -> str: + return "VALUE - {}".format(self._value) + class Flow(FIGNode): def __init__(self, id) -> None: diff --git a/lfr/fig/fluidinteractiongraph.py b/lfr/fig/fluidinteractiongraph.py index 23174cd..4f60e0e 100644 --- a/lfr/fig/fluidinteractiongraph.py +++ b/lfr/fig/fluidinteractiongraph.py @@ -1,10 +1,11 @@ from __future__ import annotations import copy -from typing import Dict, List import networkx as nx +from lfr.compiler.distribute.statetable import StateTable +from typing import List, Dict, Union from lfr.fig.fignode import ( FIGNode, IONode, @@ -38,9 +39,14 @@ def __init__(self, data=None, val=None, **attr) -> None: # self._fluid_interactions = dict() self._gen_id = 0 self._annotations_reverse_map: Dict[ - FIGNode, List[DistributeAnnotation] + Union[FIGNode, DistributeAnnotation], List[DistributeAnnotation] ] = dict() self._annotations: List[DistributeAnnotation] = [] + # Use this to store all the control to flow logic + self._state_tables: List[StateTable] = [] + + def add_state_table(self, state_table) -> None: + self._state_tables.append(state_table) @property def annotations(self) -> List[DistributeAnnotation]: @@ -48,6 +54,7 @@ def annotations(self) -> List[DistributeAnnotation]: def add_fignode(self, node: FIGNode) -> None: self._fignodes[node.id] = node + self._annotations_reverse_map[node] = [] self.add_node(node.id) def get_fignode(self, id: str) -> FIGNode: @@ -63,6 +70,15 @@ def load_fignodes(self, fig_nodes: List[FIGNode]) -> None: for node in fig_nodes: self._fignodes[node.id] = node + # Add an entry for the reverse map here to make things simpler + self._annotations_reverse_map[node] = [] + + def load_annotations(self, annotations: List[DistributeAnnotation]) -> None: + self._annotations.extend(annotations) + for annotation in self._annotations: + for item in annotation.get_items(): + self.__add_to_reverse_map(item, annotation) + def contains_fignode(self, fluid_object: FIGNode) -> bool: return fluid_object.id in self._fignodes.keys() @@ -80,6 +96,10 @@ def rename_nodes(self, rename_map: Dict[str, str]) -> None: nx.relabel_nodes(self, rename_map, False) + def rename_annotations(self, rename_map: Dict[str, str]) -> None: + for annotation in self._annotations: + annotation.rename(rename_map[annotation.id]) + def add_interaction(self, interaction: Interaction): if interaction.id not in self._fignodes.keys(): self._fignodes[interaction.id] = interaction @@ -88,6 +108,9 @@ def add_interaction(self, interaction: Interaction): "Interaction already present in the FIG: {0}".format(interaction.id) ) + # Initialize this for the interaction + self._annotations_reverse_map[interaction] = [] + if isinstance(interaction, FluidFluidInteraction): self.__add_fluid_fluid_interaction(interaction) @@ -120,11 +143,12 @@ def connect_fignodes(self, source: FIGNode, target: FIGNode): self.add_edge(source.id, target.id) def get_interactions(self) -> List[Interaction]: - return [ - self._fignodes[key] - for key in self._fignodes - if isinstance(self._fignodes[key], Interaction) - ] + ret = [] + for item in self._fignodes.values(): + if isinstance(item, Interaction): + ret.append(item) + + return ret @property def io(self) -> List[IONode]: @@ -136,46 +160,54 @@ def io(self) -> List[IONode]: return ret - def get_annotation(self, fig_node: FIGNode) -> List[DistributeAnnotation]: + def get_fig_annotations(self, fig_node: FIGNode) -> List[DistributeAnnotation]: return self._annotations_reverse_map[fig_node] def add_and_annotation(self, nodes: List[FIGNode]) -> ANDAnnotation: - print("Need to implement the generation of the AND annotations") - fig_node_name = "DIST_AND_" + str(uuid.uuid4()) - annotation = ANDAnnotation(fig_node_name) + annotation_name = "DIST_AND_" + str(uuid.uuid4()) + print( + "Adding DIST-AND annotation '{}' for fig nodes {}".format( + annotation_name, ", ".join([node.id for node in nodes]) + ) + ) + annotation = ANDAnnotation(annotation_name) self._annotations.append(annotation) + self._annotations_reverse_map[annotation] = [] for fignode in nodes: - annotation.add_fignode(fignode) - if fignode in self._annotations_reverse_map.keys(): - self._annotations_reverse_map[fignode].append(annotation) - else: - self._annotations_reverse_map[fignode] = [annotation] + annotation.add_annotated_item(fignode) + self.__add_to_reverse_map(fignode, annotation) return annotation - def add_or_annotation(self, nodes: List[FIGNode]) -> ORAnnotation: - print("Need to implement the generation of the OR annotation") - fig_node_name = "DIST_OR_" + str(uuid.uuid4()) - annotation = ORAnnotation(fig_node_name) + def add_or_annotation( + self, constrained_items: List[Union[FIGNode, DistributeAnnotation]] + ) -> ORAnnotation: + annotation_name = "DIST_OR_" + str(uuid.uuid4()) + print( + "Adding DIST-OR annotation '{}' for fig nodes {}".format( + annotation_name, ", ".join([node.id for node in constrained_items]) + ) + ) + annotation = ORAnnotation(annotation_name) self._annotations.append(annotation) - for fignode in nodes: - annotation.add_fignode(fignode) - if fignode in self._annotations_reverse_map.keys(): - self._annotations_reverse_map[fignode].append(annotation) - else: - self._annotations_reverse_map[fignode] = [annotation] + self._annotations_reverse_map[annotation] = [] + for item in constrained_items: + annotation.add_annotated_item(item) + self.__add_to_reverse_map(item, annotation) return annotation def add_not_annotation(self, nodes: List[FIGNode]) -> NOTAnnotation: - print("Need to implement the generation of the NOT annotation") - fig_node_name = "DIST_NOT_" + str(uuid.uuid4()) - annotation = NOTAnnotation(fig_node_name) + annotation_name = "DIST_NOT_" + str(uuid.uuid4()) + print( + "Adding DIST-AND annotation '{}' for fig nodes {}".format( + annotation_name, ", ".join([node.id for node in nodes]) + ) + ) + annotation = NOTAnnotation(annotation_name) self._annotations.append(annotation) + self._annotations_reverse_map[annotation] = [] for fignode in nodes: - annotation.add_fignode(fignode) - if fignode in self._annotations_reverse_map.keys(): - self._annotations_reverse_map[fignode].append(annotation) - else: - self._annotations_reverse_map[fignode] = [annotation] + annotation.add_annotated_item(fignode) + self.__add_to_reverse_map(fignode, annotation) return annotation def add_fig(self, fig_to_add: FluidInteractionGraph) -> None: @@ -189,6 +221,9 @@ def add_fig(self, fig_to_add: FluidInteractionGraph) -> None: for edge in fig_to_add.edges: self.add_edge(edge[0], edge[1]) + # TODO - Verify if the cloned annotations are correct or not + self.load_annotations(fig_to_add.annotations) + def get_input_fignodes(self) -> List[IONode]: ret = [] for fignode in self._fignodes.values(): @@ -201,9 +236,13 @@ def get_input_fignodes(self) -> List[IONode]: def __str__(self): return self.edges.__str__() + def copy(self, as_view): + return super().copy(as_view=as_view) + def __deepcopy__(self, memo=None): if memo is None: memo = {} + not_there = [] existing = memo.get(self, not_there) if existing is not not_there: @@ -212,13 +251,42 @@ def __deepcopy__(self, memo=None): fignodes_copy = copy.deepcopy( [self._fignodes[key] for key in self._fignodes], memo ) + fig_copy = self.copy(as_view=False) fig_copy.__class__ = FluidInteractionGraph fig_copy.load_fignodes(fignodes_copy) + + # Now since all the annotations are loaded up, copy the right cloned references to fig nodes for the constriants + figannotations_copy = [] + + for current_annotation in self._annotations: + for current_fignode in current_annotation.get_items(): + copy_fignode = fig_copy.get_fignode(current_fignode.id) + copy_annotation = copy.copy(current_annotation) + copy_annotation.add_annotated_item(copy_fignode) + figannotations_copy.append(copy_annotation) + + fig_copy.load_annotations(figannotations_copy) return fig_copy # ---------- HELPER METHODS ----------- + def __add_to_reverse_map( + self, + item: Union[FIGNode, DistributeAnnotation], + annotation: DistributeAnnotation, + ) -> None: + if self._annotations_reverse_map is None: + self._annotations_reverse_map = dict() + + if item in self._annotations_reverse_map.keys(): + self._annotations_reverse_map[item].append(annotation) + else: + if annotation in self._annotations_reverse_map[item]: + raise Exception("Annotation already present in the reverse map !") + + self._annotations_reverse_map[item] = [annotation] + def __get_val_node_id(self) -> str: self._gen_id += 1 return "val_{0}".format(self._gen_id) diff --git a/lfr/graphmatch/figmappingmatcher.py b/lfr/graphmatch/figmappingmatcher.py index dc62762..f6abd51 100644 --- a/lfr/graphmatch/figmappingmatcher.py +++ b/lfr/graphmatch/figmappingmatcher.py @@ -1,3 +1,4 @@ +from lfr.fig.fignode import FIGNode from typing import Dict from networkx.algorithms.isomorphism import DiGraphMatcher from networkx.classes import digraph @@ -17,6 +18,7 @@ def __init__( G2: digraph.DiGraph, semantic_information: Dict[str, NodeFilter], ): + self._fig = G1 self._semantic_information = semantic_information super(FIGMappingMatcher, self).__init__(G1, G2) @@ -55,6 +57,12 @@ def semantic_feasibility(self, G1_node, G2_node): should consider multigraphs. """ - # TODO - Get the semantic information from here and then use it to figure out if its right type of node or not - raise NotImplementedError() - return True + # Get the semantic information from here and then use it to figure out if its right type of node or not + # Figure out if G1 or G2 is the pattern graph + g1_fig_info = self._fig.get_fignode(G1_node) + g2_semantic_info = self._semantic_information[G2_node] + + if g2_semantic_info.is_valid_node_type(g1_fig_info.match_string): + return True + else: + return False diff --git a/lfr/graphmatch/interface.py b/lfr/graphmatch/interface.py index 9d32313..9f5cbad 100644 --- a/lfr/graphmatch/interface.py +++ b/lfr/graphmatch/interface.py @@ -1,28 +1,48 @@ -from networkx.classes.function import restricted_view +from networkx.classes.digraph import DiGraph from lfr.graphmatch.figmappingmatcher import FIGMappingMatcher from typing import Dict from lfr.netlistgenerator.mappinglibrary import MappingLibrary from lfr.fig.fluidinteractiongraph import FluidInteractionGraph -from reggie.matchpattern import MatchPattern +from reggie import MatchPattern +from reggie import NodeFilter -def match_node_constraints(semantic_information, subgraph, mapping) -> bool: +def match_node_constraints( + fig: FluidInteractionGraph, + structural_template, + semantic_information: Dict[str, NodeFilter], + subgraph, + mapping, +) -> bool: # TODO - Check if the constraints match for the subgraph # Loop through each of the unique constraints in the overall semantic information # Check if the 1) Constraint type matches, 2) if the same nodes in the GM.mapping # match the ones denoted by the subgraph LFR - raise NotImplementedError() + # Extract the subgraph pairings in the to know what fig node matches what matchnode + pattern_graph_coloring_map = dict() + pattern_graph_node_filters = dict() -def extract_subgraph_mapping(subgraph, mapping) -> Dict[str, str]: - ret = dict() - raise NotImplementedError() - return ret + for fig_vertex_id, parttern_vertex_id in mapping.items(): + # Get Constraints for fig_vertex_id and the NodeFilter for the pattern_vertex_id + fig_node = fig.get_fignode(fig_vertex_id) + node_filter = semantic_information[parttern_vertex_id] + + # Load up all the constraints for this particular fignode + pattern_graph_coloring_map[fig_node] = node_filter.get_constriants() + pattern_graph_node_filters[fig_node] = node_filter + + # TODO - Now check to see if all the constraints are a match in node filter + for fig_node in pattern_graph_coloring_map.keys(): + # Get the constraints on the fig + fig_constraints = fig.get_fig_annotations(fig_node) + return True -def match(fig: FluidInteractionGraph, library: MappingLibrary): + +def get_fig_matches(fig: FluidInteractionGraph, library: MappingLibrary): patterns: Dict[ - str, object + str, MatchPattern ] = dict() # Store the mint and the match pattern object here ret = [] @@ -30,21 +50,28 @@ def match(fig: FluidInteractionGraph, library: MappingLibrary): # TODO - Retrun the networkx subgraph views of the of the FIG # Step 1 - Generate the match candidates by running the subgraph isomerism for all the items stored in the library for (mint, match_pattern_string) in library.get_match_patterns(): + if match_pattern_string == "" or match_pattern_string is None: + print("Warning ! - Missing match string for mint- {}".format(mint)) + continue pattern = MatchPattern(match_pattern_string) patterns[mint] = pattern structural_template = pattern.get_structural_template() semantic_information = pattern.get_semantic_template() GM = FIGMappingMatcher(fig, structural_template, semantic_information) + for subgraph in GM.subgraph_isomorphisms_iter(): - print(subgraph) - # TODO - Not sure what, but work with these subgraphs at the end and then push them through the constraint checking phase + # Work with these subgraphs at the end and then push them through the constraint checking phase # This would be a match, figure out how to get the mapping from GM.mapping # Loop through each of the candates # TODO - Compare the constraints on all the nodes for this for subgraph to confirm the match - if match_node_constraints(semantic_information, subgraph, GM.mapping): + if match_node_constraints( + fig, structural_template, semantic_information, subgraph, GM.mapping + ): # TODO - Extract the specific mapping for the subgraph - subgraph_mapping = extract_subgraph_mapping(subgraph, GM.mapping) - ret.append((subgraph, subgraph_mapping)) + print("Found Match: {}".format(mint)) + print(subgraph) + + ret.append((mint, subgraph)) return ret diff --git a/lfr/netlistgenerator/v2/constructionnode.py b/lfr/netlistgenerator/v2/constructionnode.py index ea468da..119f9b5 100644 --- a/lfr/netlistgenerator/v2/constructionnode.py +++ b/lfr/netlistgenerator/v2/constructionnode.py @@ -72,6 +72,7 @@ def use_explicit_mapping(self, mapping: MappingOption) -> None: # Now that we have overwritten all the netlist options here # we basically cherry pick the one little bit that we want to attach here self._mapping_options.append(mapping) + self.load_connection_options() def add_mapping_option(self, mapping_option: MappingOption) -> None: if self._explict_mapping_flag is True: diff --git a/lfr/netlistgenerator/v2/generator.py b/lfr/netlistgenerator/v2/generator.py index d283821..55f4a87 100644 --- a/lfr/netlistgenerator/v2/generator.py +++ b/lfr/netlistgenerator/v2/generator.py @@ -1,13 +1,16 @@ import sys from copy import deepcopy from typing import List, Set +import networkx as nx + +from pymint.mintdevice import MINTDevice +from lfr.graphmatch.interface import get_fig_matches from pymint.mintlayer import MINTLayerType from pymint import MINTDevice from lfr.compiler.module import Module from lfr.fig.fignode import IOType, Pump, Storage, ValueNode -from lfr.fig.fluidinteractiongraph import FluidInteractionGraph from lfr.fig.interaction import ( FluidIntegerInteraction, FluidNumberInteraction, @@ -19,7 +22,6 @@ from lfr.netlistgenerator.v2.connectingoption import ConnectingOption from lfr.netlistgenerator.v2.constructiongraph import ConstructionGraph from lfr.netlistgenerator.v2.constructionnode import ConstructionNode -from lfr.netlistgenerator.v2.gen_strategies.dropxstrategy import DropXStrategy from lfr.netlistgenerator.v2.gen_strategies.dummy import DummyStrategy from lfr.netlistgenerator.v2.gen_strategies.genstrategy import GenStrategy from lfr.netlistgenerator.v2.gen_strategies.marsstrategy import MarsStrategy @@ -28,8 +30,6 @@ NetworkMappingOption, NetworkMappingOptionType, ) -from lfr.netlistgenerator.v2.procedural_component_algorithms.ytree import YTREE -from lfr.postprocessor.mapping import NetworkMapping, NodeMappingTemplate # def generate_MARS_library() -> MappingLibrary: # # TODO - Programatically create each of the items necessary for the MARS primitive library, @@ -332,7 +332,9 @@ def generate_dropx_library() -> MappingLibrary: port = Primitive( "PORT", PrimitiveType.COMPONENT, - "IO", + r"""{ + v1:IO + }""", False, False, port_inputs, @@ -361,7 +363,9 @@ def generate_dropx_library() -> MappingLibrary: pico_injector = Primitive( "PICOINJECTOR", PrimitiveType.COMPONENT, - "MIX", + r"""{ + v1:MIX + }""", False, False, pico_injector_inputs, @@ -391,7 +395,9 @@ def generate_dropx_library() -> MappingLibrary: electrophoresis_merger = Primitive( "DROPLET MERGER", PrimitiveType.COMPONENT, - "MIX", + r"""{ + v1:MIX + }""", False, False, electrophoresis_merger_inputs, @@ -421,7 +427,9 @@ def generate_dropx_library() -> MappingLibrary: droplet_sorter = Primitive( "DROPLET SORTER", PrimitiveType.COMPONENT, - "SIEVE", + r"""{ + v1:SIEVE + }""", False, False, droplet_sorter_inputs, @@ -449,7 +457,9 @@ def generate_dropx_library() -> MappingLibrary: droplet_generator = Primitive( "NOZZLE DROPLET GENERATOR", PrimitiveType.NETLIST, - "METER", + r"""{ + v1:METER + }""", False, False, droplet_generator_inputs, @@ -487,7 +497,9 @@ def generate_dropx_library() -> MappingLibrary: droplet_merger_junction = Primitive( "DROPLET MERGER JUNCTION", PrimitiveType.COMPONENT, - "MIX", + r"""{ + v1:MIX + }""", False, False, droplet_merger_junction_inputs, @@ -515,7 +527,9 @@ def generate_dropx_library() -> MappingLibrary: droplet_merger_channel = Primitive( "DROPLET MERGER CHANNEL", PrimitiveType.COMPONENT, - "MIX", + r"""{ + v1:MIX + }""", False, False, droplet_merger_channel_inputs, @@ -552,7 +566,9 @@ def generate_dropx_library() -> MappingLibrary: cf_mixer = Primitive( "MIXER", PrimitiveType.COMPONENT, - "MIX", + r"""{ + v1:MIX + }""", False, False, cf_mixer_inputs, @@ -581,7 +597,9 @@ def generate_dropx_library() -> MappingLibrary: droplet_splitter = Primitive( "DROPLET SPLITTER", PrimitiveType.COMPONENT, - "DIVIDE", + r"""{ + v1:DIVIDE + }""", False, False, droplet_splitter_inputs, @@ -627,7 +645,9 @@ def generate_dropx_library() -> MappingLibrary: mixer = Primitive( "MIXER", PrimitiveType.COMPONENT, - "MIX", + r"""{ + v1:MIX + }""", False, False, mixer_inputs, @@ -655,7 +675,9 @@ def generate_dropx_library() -> MappingLibrary: droplet_capacitance_sensor = Primitive( "DROPLET CAPACITANCE SENSOR", PrimitiveType.COMPONENT, - "PROCESS", + r"""{ + v1:PROCESS + }""", False, False, droplet_capacitance_sensor_inputs, @@ -685,7 +707,9 @@ def generate_dropx_library() -> MappingLibrary: filter = Primitive( "FILTER", PrimitiveType.COMPONENT, - "PROCESS", + r"""{ + v1:PROCESS + }""", False, False, filter_inputs, @@ -713,7 +737,9 @@ def generate_dropx_library() -> MappingLibrary: droplet_fluorescence_sensor = Primitive( "DROPLET FLUORESCENCE SENSOR", PrimitiveType.COMPONENT, - "PROCESS", + r"""{ + v1:PROCESS + }""", False, False, droplet_fluorescence_sensor_inputs, @@ -740,9 +766,11 @@ def generate_dropx_library() -> MappingLibrary: droplet_luminescence_sensor_carriers = [] droplet_luminescence_sensor = Primitive( - "DROPLET CAPACITANCE SENSOR", + "DROPLET LUMINESCENCE SENSOR", PrimitiveType.COMPONENT, - "PROCESS", + r"""{ + v1:PROCESS + }""", False, False, droplet_luminescence_sensor_inputs, @@ -772,7 +800,9 @@ def generate_dropx_library() -> MappingLibrary: droplet_spacer = Primitive( "DROPLET SPACER", PrimitiveType.NETLIST, - "PROCESS", + r"""{ + v1:PROCESS + }""", False, False, droplet_spacer_inputs, @@ -823,9 +853,14 @@ def generate(module: Module, library: MappingLibrary) -> MINTDevice: # # FUTURE WORK # - # Do the regex matching to find the mapping options + # Do the reggie matching to find the mapping options # This means that we might need to have a forest of construction of graphs # as there would be alternatives for each type of mapping + matches = get_fig_matches(module.FIG, library) + for match in matches: + print(match) + + # Map the interactions in the fig to individual library options for interaction in module.FIG.get_interactions(): operator_candidates = library.get_operators(interaction_type=interaction.type) cn = ConstructionNode(interaction.id) @@ -855,7 +890,8 @@ def generate(module: Module, library: MappingLibrary) -> MINTDevice: # Generate all ports necessary for the Explicitly declared IO # ------- # Generate the flow layer IO. These are typically declared explicitly - # TODO - Figure out how we should generate the construction nodes for control networks + # TODO - Figure out how we should generate the construction nodes for control + # networks for io_ref in module.io: if io_ref.type is IOType.CONTROL: @@ -892,10 +928,12 @@ def generate(module: Module, library: MappingLibrary) -> MINTDevice: mappings = module.get_explicit_mappings() override_network_mappings(mappings, library, module.FIG, construction_graph) - # TODO - Go through the different flow-flow edge networks to generate construction nodes - # specific to these networks, Conditions: - # if its a 1-1 flow-flow connection, then create a construction node for the two flow nodes - # if its a 1-n / n-1 / n-n construction nodes, then create a construction node capturing the whole network + # TODO - Go through the different flow-flow edge networks to generate construction + # nodes specific to these networks, Conditions: + # if its a 1-1 flow-flow connection, then create a construction node for the two + # flow nodes + # if its a 1-n / n-1 / n-n construction nodes, then create a construction node + # capturing the whole network # TODO - Deal with coverage issues here since we need to figure out what are the flow networks, # that we want to match first and then ensure that they're no included on any list @@ -910,7 +948,8 @@ def generate(module: Module, library: MappingLibrary) -> MINTDevice: # Find all the explicit mappings and override them in the construction graph override_mappings(mappings, library, module.FIG, construction_graph) - # Whittle Down the mapping options here to only include the requried single candidates + # Whittle Down the mapping options here to only include the requried single + # candidates # TODO - Check what library is being used and use the required library here active_strategy.reduce_mapping_options() @@ -952,10 +991,12 @@ def override_mappings( fig: FluidInteractionGraph, construction_graph: ConstructionGraph, ) -> None: - # Go through the entire set of mappings in the FIG and generate / append the mapping options + # Go through the entire set of mappings in the FIG and generate / append the + # mapping options # Step 1 - Loop through each of the mappingtemplates # Step 2 - Loop through each of the instances in teh mappingtemplate - # Step 3 - Find the cn associated with each of the fig nodes and override the explicit mapping if mappingtemplate has an associated technology string + # Step 3 - Find the cn associated with each of the fig nodes and override the + # explicit mapping if mappingtemplate has an associated technology string for mapping in mappings: for instance in mapping.instances: @@ -988,10 +1029,11 @@ def override_mappings( # options # Step 2 - In there is a string assiciated with the mappingtemplate, we - # eliminate all mapping options that dont have a matching string / generate - # a mapping option with the corresponding + # eliminate all mapping options that dont have a matching string / + # generate a mapping option with the corresponding - # In the case of an Fluid Value interaction put all valuenodes in the subgraph + # In the case of an Fluid Value interaction put all valuenodes in the + # subgraph node_ids.extend( [ fig.get_fignode(edge[0]).id @@ -1030,10 +1072,12 @@ def override_network_mappings( fig: FluidInteractionGraph, construction_graph: ConstructionGraph, ) -> None: - # Go through the entire set of mappings in the FIG and generate / append the mapping options + # Go through the entire set of mappings in the FIG and generate / append the + # mapping options # Step 1 - Loop through each of the mappingtemplates # Step 2 - Loop through each of the instances in teh mappingtemplate - # Step 3 - Find the cn associated with each of the fig nodes and override the explicit mapping if mappingtemplate has an associated technology string + # Step 3 - Find the cn associated with each of the fig nodes and override the + # explicit mapping if mappingtemplate has an associated technology string assign_node_index = 0 for mapping in mappings: for instance in mapping.instances: @@ -1174,18 +1218,19 @@ def eliminate_passthrough_nodes(construction_graph: ConstructionGraph): in_points = [in_edge[0] for in_edge in in_edges] out_points = [out_edge[1] for out_edge in out_edges] - # Delete the node - construction_graph.delete_node(node_id) - # Create edges for the different cases # Case 1 - 1->1 if len(in_points) == 1 and len(out_points) == 1: + # Delete the node + construction_graph.delete_node(node_id) construction_graph.add_edge(in_points[0], out_points[0]) # Case 2 - n->1 # Case 3 - 1->n elif (len(in_points) > 1 and len(out_points) == 1) or ( len(in_points) == 1 and len(out_points) > 1 ): + # Delete the node + construction_graph.delete_node(node_id) for in_point in in_points: for out_point in out_points: construction_graph.add_edge(in_point, out_point) @@ -1239,8 +1284,8 @@ def get_flow_flow_candidates( # Step 1. Do a shallow copy of the graph fig_original = module.FIG - fig_copy = ( - module.FIG.copy() + fig_copy = module.FIG.copy( + as_view=False ) # Note this does not copy anything besides the nx.DiGraph at the moment # Step 2. Remove all the fignodes that are not Flow @@ -1270,8 +1315,8 @@ def get_flow_flow_candidates( print("Flow candidate") print(component) sub = fig_original.subgraph(component) - # TODO - Decide what the mapping type should be. for now assume that we just a single - # passthrough type scenario where we don't have to do much work + # TODO - Decide what the mapping type should be. for now assume that we just a + # single passthrough type scenario where we don't have to do much work is_passthrough = __check_if_passthrough(sub) if is_passthrough: mapping_type = NetworkMappingOptionType.PASS_THROUGH diff --git a/lfr/utils.py b/lfr/utils.py index 108f7d4..16837d4 100644 --- a/lfr/utils.py +++ b/lfr/utils.py @@ -1,6 +1,7 @@ import json import os +from typing import List from networkx import nx from pymint.mintdevice import MINTDevice @@ -8,10 +9,13 @@ def printgraph(G, filename: str) -> None: + # Generate labels and whatnot for the graph + H = G.copy(as_view=False) + # Print out the dot file and then run the conversion tt = os.path.join(parameters.OUTPUT_DIR, filename) print("output:", parameters.OUTPUT_DIR) print("output:", tt) - nx.nx_agraph.to_agraph(G).write(tt) + nx.nx_agraph.to_agraph(H).write(tt) os.system("dot -Tpdf {} -o {}.pdf".format(tt, tt)) @@ -35,3 +39,16 @@ def print_netlist(device: MINTDevice) -> None: mint_file = open(get_ouput_path(device.name + ".mint"), "wt") mint_file.write(minttext) mint_file.close() + + +def convert_list_to_str(lst: List) -> str: + """Returns a string list formatted as a string + + Args: + lst (List): list we need to convert into a string + + Returns: + str: list formatted as a string + """ + ret = "[{}]".format(", ".join([str(i) for i in lst])) + return ret diff --git a/scripts/all.sh b/scripts/all.sh new file mode 100755 index 0000000..1791557 --- /dev/null +++ b/scripts/all.sh @@ -0,0 +1,104 @@ +#!/bin/sh + + +echo "Synthesizing DropX designs" + +for f in ~/CIDAR/LFR-Testcases/dropx/*.lfr; + +do + echo "Running File $f"; + lfr-compile $f --no-gen --outpath ~/CIDAR/LFR-Dry-Run/dropx/ +done + + +echo "Synthesizing Cassie Thesis Designs" + +for f in ~/CIDAR/LFR-Testcases/chthesis/*.lfr; + +do + echo "Running File $f"; + lfr-compile $f --no-gen --outpath ~/CIDAR/LFR-Dry-Run/chthesis/ +done + + + +echo "Synthesizing COVID" + +for f in ~/CIDAR/LFR-Testcases/COVID/*.lfr; + +do + echo "Running File $f"; + lfr-compile $f --no-gen --outpath ~/CIDAR/LFR-Dry-Run/COVID/ +done + + +echo "Synthesizing Expressions" + +for f in ~/CIDAR/LFR-Testcases/Expressions/*.lfr; + +do + echo "Running File $f"; + lfr-compile $f --no-gen --outpath ~/CIDAR/LFR-Dry-Run/Expressions/ +done + +echo "Synthesizing ghissues" + +for f in ~/CIDAR/LFR-Testcases/ghissues/*.lfr; + +do + echo "Running File $f"; + lfr-compile $f --no-gen --outpath ~/CIDAR/LFR-Dry-Run/ghissues/ +done + + + +echo "Synthesizing Graph Coverage" + +for f in ~/CIDAR/LFR-Testcases/GraphCoverage/*.lfr; + +do + echo "Running File $f"; + lfr-compile $f --no-gen --outpath ~/CIDAR/LFR-Dry-Run/GraphCoverage/ +done + + +echo "Synthesizing Parser Test" + +for f in ~/CIDAR/LFR-Testcases/ParserTest/*.lfr; + +do + echo "Running File $f"; + lfr-compile $f --no-gen --outpath ~/CIDAR/LFR-Dry-Run/ParserTest/ +done + + + +echo "Synthesizing Protocols" + +for f in ~/CIDAR/LFR-Testcases/Protocols/*.lfr; + +do + echo "Running File $f"; + lfr-compile $f --no-gen --outpath ~/CIDAR/LFR-Dry-Run/Protocols/ +done + + +echo "Synthesizing Ryuichi's Designs" + +for f in ~/CIDAR/LFR-Testcases/Ryuichi\'s\ designs/*.lfr; + +do + echo "Running File $f"; + lfr-compile $f --no-gen --outpath ~/CIDAR/LFR-Dry-Run/Ryuichi\'s\ designs/ +done + + +echo "Synthesizing Technology Mapping" + +for f in ~/CIDAR/LFR-Testcases/TechnologyMapping/*.lfr; + +do + echo "Running File $f"; + lfr-compile $f --no-gen --outpath ~/CIDAR/LFR-Dry-Run/TechnologyMapping/ +done + From 2716b60ac479848524e21d8c9173d0fabda1a155 Mon Sep 17 00:00:00 2001 From: Radhakrishna Sanka Date: Wed, 7 Apr 2021 19:59:59 -0400 Subject: [PATCH 08/36] Updated imports to prevent cyclic import --- lfr/compiler/distribute/statetable.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lfr/compiler/distribute/statetable.py b/lfr/compiler/distribute/statetable.py index 3d83a95..9eddc1f 100644 --- a/lfr/compiler/distribute/statetable.py +++ b/lfr/compiler/distribute/statetable.py @@ -1,7 +1,11 @@ from __future__ import annotations +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from lfr.fig.fluidinteractiongraph import FluidInteractionGraph + from lfr.utils import convert_list_to_str from lfr.fig.annotation import ANDAnnotation, NOTAnnotation, ORAnnotation -from lfr.fig.fluidinteractiongraph import FluidInteractionGraph from typing import Dict, List, Tuple import networkx as nx From 5dd7bf0c4469140fa1404a115499f5275dc4d546 Mon Sep 17 00:00:00 2001 From: Radhakrishna Sanka Date: Wed, 7 Apr 2021 20:54:24 -0400 Subject: [PATCH 09/36] Updated the annotations datastructure and changed the field to have the properties to store the tuple of of fignodes rather than just 1 fignode for the annotation. Also updated all the fig copy function methods on that account --- lfr/fig/annotation.py | 16 ++-- lfr/fig/fluidinteractiongraph.py | 125 +++++++++++++++++++++---------- 2 files changed, 95 insertions(+), 46 deletions(-) diff --git a/lfr/fig/annotation.py b/lfr/fig/annotation.py index ed37436..086bb1d 100644 --- a/lfr/fig/annotation.py +++ b/lfr/fig/annotation.py @@ -1,12 +1,14 @@ from __future__ import annotations -from typing import List, Union +from typing import List, Tuple, Union from lfr.fig.fignode import FIGNode class DistributeAnnotation: def __init__(self, id: str) -> None: self._id = id - self._annotated_items: List[Union[FIGNode, DistributeAnnotation]] = [] + self._annotated_items: List[ + Union[Tuple[FIGNode, FIGNode], DistributeAnnotation] + ] = [] @property def id(self) -> str: @@ -15,13 +17,17 @@ def id(self) -> str: def rename(self, new_id: str) -> None: self._id = new_id - def add_annotated_item(self, item: Union[FIGNode, DistributeAnnotation]) -> None: + def add_annotated_item( + self, item: Union[Tuple[FIGNode, FIGNode], DistributeAnnotation] + ) -> None: self._annotated_items.append(item) - def remove_item(self, item: Union[FIGNode, DistributeAnnotation]) -> None: + def remove_item( + self, item: Union[Tuple[FIGNode, FIGNode], DistributeAnnotation] + ) -> None: self._annotated_items.remove(item) - def get_items(self) -> List[Union[FIGNode, DistributeAnnotation]]: + def get_items(self) -> List[Union[Tuple[FIGNode, FIGNode], DistributeAnnotation]]: return self._annotated_items def clear_fignodes(self) -> None: diff --git a/lfr/fig/fluidinteractiongraph.py b/lfr/fig/fluidinteractiongraph.py index 4f60e0e..c6d2328 100644 --- a/lfr/fig/fluidinteractiongraph.py +++ b/lfr/fig/fluidinteractiongraph.py @@ -5,7 +5,7 @@ import networkx as nx from lfr.compiler.distribute.statetable import StateTable -from typing import List, Dict, Union +from typing import List, Dict, Tuple, Union from lfr.fig.fignode import ( FIGNode, IONode, @@ -77,7 +77,11 @@ def load_annotations(self, annotations: List[DistributeAnnotation]) -> None: self._annotations.extend(annotations) for annotation in self._annotations: for item in annotation.get_items(): - self.__add_to_reverse_map(item, annotation) + if isinstance(item, DistributeAnnotation): + self.__add_to_reverse_map(item, annotation) + else: + self.__add_to_reverse_map(item[0], annotation) + self.__add_to_reverse_map(item[1], annotation) def contains_fignode(self, fluid_object: FIGNode) -> bool: return fluid_object.id in self._fignodes.keys() @@ -163,51 +167,60 @@ def io(self) -> List[IONode]: def get_fig_annotations(self, fig_node: FIGNode) -> List[DistributeAnnotation]: return self._annotations_reverse_map[fig_node] - def add_and_annotation(self, nodes: List[FIGNode]) -> ANDAnnotation: + def add_and_annotation( + self, fignode_tuples: List[Tuple[FIGNode, FIGNode]] + ) -> ANDAnnotation: annotation_name = "DIST_AND_" + str(uuid.uuid4()) - print( - "Adding DIST-AND annotation '{}' for fig nodes {}".format( - annotation_name, ", ".join([node.id for node in nodes]) - ) - ) + print("Adding DIST-AND annotation '{}' for fig nodes:".format(annotation_name)) + for item in fignode_tuples: + print("{}->{}".format(item[0], item[1])) annotation = ANDAnnotation(annotation_name) self._annotations.append(annotation) self._annotations_reverse_map[annotation] = [] - for fignode in nodes: - annotation.add_annotated_item(fignode) - self.__add_to_reverse_map(fignode, annotation) + for fignode_tuple in fignode_tuples: + annotation.add_annotated_item(fignode_tuple) + self.__add_to_reverse_map(fignode_tuple[0], annotation) + self.__add_to_reverse_map(fignode_tuple[1], annotation) return annotation def add_or_annotation( - self, constrained_items: List[Union[FIGNode, DistributeAnnotation]] + self, + constrained_items: List[Union[Tuple[FIGNode, FIGNode], DistributeAnnotation]], ) -> ORAnnotation: annotation_name = "DIST_OR_" + str(uuid.uuid4()) - print( - "Adding DIST-OR annotation '{}' for fig nodes {}".format( - annotation_name, ", ".join([node.id for node in constrained_items]) - ) - ) + print("Adding DIST-OR annotation '{}' for fig nodes:".format(annotation_name)) + for item in constrained_items: + if isinstance(item, DistributeAnnotation): + print("{} (Annotation)".format(item.id)) + else: + print("{}->{}".format(item[0], item[1])) + annotation = ORAnnotation(annotation_name) self._annotations.append(annotation) self._annotations_reverse_map[annotation] = [] for item in constrained_items: annotation.add_annotated_item(item) - self.__add_to_reverse_map(item, annotation) + if isinstance(item, DistributeAnnotation): + pass + else: + self.__add_to_reverse_map(item[0], annotation) + self.__add_to_reverse_map(item[1], annotation) + return annotation - def add_not_annotation(self, nodes: List[FIGNode]) -> NOTAnnotation: + def add_not_annotation( + self, fignode_tuple: Tuple[FIGNode, FIGNode] + ) -> NOTAnnotation: annotation_name = "DIST_NOT_" + str(uuid.uuid4()) - print( - "Adding DIST-AND annotation '{}' for fig nodes {}".format( - annotation_name, ", ".join([node.id for node in nodes]) - ) - ) + print("Adding DIST-AND annotation '{}' for fig nodes:".format(annotation_name)) + print("{}->{}".format(fignode_tuple[0], fignode_tuple[1])) + annotation = NOTAnnotation(annotation_name) self._annotations.append(annotation) self._annotations_reverse_map[annotation] = [] - for fignode in nodes: - annotation.add_annotated_item(fignode) - self.__add_to_reverse_map(fignode, annotation) + annotation.add_annotated_item(fignode_tuple) + self.__add_to_reverse_map(fignode_tuple[0], annotation) + self.__add_to_reverse_map(fignode_tuple[1], annotation) return annotation def add_fig(self, fig_to_add: FluidInteractionGraph) -> None: @@ -248,23 +261,50 @@ def __deepcopy__(self, memo=None): if existing is not not_there: print("ALREADY COPIED TO", repr(existing)) return existing - fignodes_copy = copy.deepcopy( - [self._fignodes[key] for key in self._fignodes], memo - ) + + fignodes_copy = [] + + # Map old_fignode <-> new_fignode + fignodes_copy_map: Dict[FIGNode, FIGNode] = dict() + + for fignode in self._fignodes.values(): + fignode_copy = copy.copy(fignode) + fignodes_copy.append(fignode_copy) + fignodes_copy_map[fignode] = fignode_copy fig_copy = self.copy(as_view=False) fig_copy.__class__ = FluidInteractionGraph + assert isinstance(fig_copy, FluidInteractionGraph) fig_copy.load_fignodes(fignodes_copy) - # Now since all the annotations are loaded up, copy the right cloned references to fig nodes for the constriants + # Now since all the annotations are loaded up, copy the right cloned + # references to fig nodes for the constriants figannotations_copy = [] + # Map old_annotatin <-> new_annotation + figannotations_copy_map: Dict[ + DistributeAnnotation, DistributeAnnotation + ] = dict() + + # Copy the annotations into the new fig copy + for current_annotation in self._annotations: + copy_annotation = copy.copy(current_annotation) + figannotations_copy.append(copy_annotation) + figannotations_copy_map[current_annotation] = copy_annotation + + # TODO - Copy the annotations items for current_annotation in self._annotations: - for current_fignode in current_annotation.get_items(): - copy_fignode = fig_copy.get_fignode(current_fignode.id) - copy_annotation = copy.copy(current_annotation) - copy_annotation.add_annotated_item(copy_fignode) - figannotations_copy.append(copy_annotation) + copy_annotation = figannotations_copy_map[current_annotation] + for annotation_item in current_annotation.get_items(): + if isinstance(annotation_item, DistributeAnnotation): + item_to_add = figannotations_copy_map[annotation_item] + copy_annotation.add_annotated_item(item_to_add) + else: + item_to_add = ( + fignodes_copy_map[annotation_item[0]], + fignodes_copy_map[annotation_item[1]], + ) + copy_annotation.add_annotated_item(item_to_add) fig_copy.load_annotations(figannotations_copy) return fig_copy @@ -279,13 +319,16 @@ def __add_to_reverse_map( if self._annotations_reverse_map is None: self._annotations_reverse_map = dict() - if item in self._annotations_reverse_map.keys(): - self._annotations_reverse_map[item].append(annotation) + if isinstance(item, DistributeAnnotation): + self.__add_to_reverse_map(item, annotation) else: - if annotation in self._annotations_reverse_map[item]: - raise Exception("Annotation already present in the reverse map !") + if item in self._annotations_reverse_map.keys(): + self._annotations_reverse_map[item].append(annotation) + else: + if annotation in self._annotations_reverse_map[item]: + raise Exception("Annotation already present in the reverse map !") - self._annotations_reverse_map[item] = [annotation] + self._annotations_reverse_map[item] = [annotation] def __get_val_node_id(self) -> str: self._gen_id += 1 From 3f0967cad1a83362f5743159a95b817f64f93ae6 Mon Sep 17 00:00:00 2001 From: Radhakrishna Sanka Date: Wed, 7 Apr 2021 20:56:46 -0400 Subject: [PATCH 10/36] Reduced problems from pylance --- lfr/compiler/module.py | 3 ++- lfr/graphmatch/figmappingmatcher.py | 1 - 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lfr/compiler/module.py b/lfr/compiler/module.py index 5d4e89d..3c540e4 100644 --- a/lfr/compiler/module.py +++ b/lfr/compiler/module.py @@ -2,7 +2,6 @@ import copy from typing import Dict, List, Optional - from lfr.compiler.moduleio import ModuleIO from lfr.fig.fignode import FIGNode, Flow, IONode, IOType from lfr.fig.fluidinteractiongraph import FluidInteractionGraph @@ -163,6 +162,7 @@ def add_fluid_numeric_interaction( elif interaction_type is InteractionType.DILUTE: finteraction = FluidNumberInteraction(fluid1, number, interaction_type) elif interaction_type is InteractionType.DIVIDE: + assert isinstance(number, int) finteraction = FluidIntegerInteraction(fluid1, number, interaction_type) else: raise Exception("Unsupported Numeric Operator") @@ -195,6 +195,7 @@ def instantiate_module( for there_node_key in io_mapping.keys(): fignode = fig_copy.get_fignode(there_node_key) # Skip if its a control type one + assert isinstance(fignode, IONode) if fignode.type is IOType.CONTROL: continue # Convert this node into a flow node diff --git a/lfr/graphmatch/figmappingmatcher.py b/lfr/graphmatch/figmappingmatcher.py index f6abd51..1fef6f2 100644 --- a/lfr/graphmatch/figmappingmatcher.py +++ b/lfr/graphmatch/figmappingmatcher.py @@ -1,4 +1,3 @@ -from lfr.fig.fignode import FIGNode from typing import Dict from networkx.algorithms.isomorphism import DiGraphMatcher from networkx.classes import digraph From 66a76ddbb7350592b89290ae29c4cfcd45771490 Mon Sep 17 00:00:00 2001 From: Radhakrishna Sanka Date: Wed, 7 Apr 2021 20:59:39 -0400 Subject: [PATCH 11/36] Updated the statetable<->annotation interface and its now updated to work with fignode_tuples instead of single fignodes --- lfr/compiler/distribute/statetable.py | 33 ++++++++++++++++++--------- lfr/graphmatch/interface.py | 1 - 2 files changed, 22 insertions(+), 12 deletions(-) diff --git a/lfr/compiler/distribute/statetable.py b/lfr/compiler/distribute/statetable.py index 9eddc1f..4236427 100644 --- a/lfr/compiler/distribute/statetable.py +++ b/lfr/compiler/distribute/statetable.py @@ -13,8 +13,6 @@ from tabulate import tabulate from lfr.compiler.distribute.BitVector import BitVector -from lfr.fig.fignode import ANDAnnotation, NOTAnnotation, ORAnnotation -from lfr.fig.fluidinteractiongraph import FluidInteractionGraph class StateTable: @@ -105,7 +103,7 @@ def generate_connectivity_table(self) -> None: column_size = len(list(full_connectivity_graph.edges)) for edge in list(full_connectivity_graph.edges): - edge_name = self.__convert_edge_to_name(edge) + edge_name = self.convert_edge_to_name(edge) connectivy_column_headers.append(edge_name) self._connectivity_edges[edge_name] = edge @@ -189,14 +187,19 @@ def generate_and_annotations(self, fig: FluidInteractionGraph) -> None: ) fig.connect_fignodes(source_node, target_node) - origin_nodes = [fig.get_fignode(edge[0]) for edge in candidate] + # origin_nodes = [fig.get_fignode(edge[0]) for edge in candidate] + tuple_names = [self.convert_edge_to_name(edge) for edge in candidate] print( "Added AND annotation on FIG: {}".format( - convert_list_to_str(origin_nodes) + convert_list_to_str(tuple_names) ) ) - assert origin_nodes is not None - annotation = fig.add_and_annotation(origin_nodes) + + fignode_tuples = [ + (fig.get_fignode(edge[0]), fig.get_fignode(edge[1])) + for edge in candidate + ] + annotation = fig.add_and_annotation(fignode_tuples) self._and_annotations.append(annotation) def generate_or_annotations(self, fig: FluidInteractionGraph) -> None: @@ -319,7 +322,7 @@ def generate_not_annotations(self, fig: FluidInteractionGraph) -> None: ) ) fig.connect_fignodes(source_node, target_node) - annotation = fig.add_not_annotation([source_node, target_node]) + annotation = fig.add_not_annotation((source_node, target_node)) self._not_annotations.append(annotation) def compute_control_mapping(self) -> None: @@ -350,11 +353,19 @@ def __ones_count(vec1) -> int: return ret @staticmethod - def __convert_edge_to_name(edge: Tuple[str, str]) -> str: + def convert_edge_to_name(edge: Tuple[str, str]) -> str: + """Generate the name of the edge to a string for printing purposes + + Args: + edge (Tuple[str, str]): This is the networkx edge that we want to convert + + Returns: + str: String representation of edge for printing + """ return "{}->{}".format(edge[0], edge[1]) def __update_connectivity_matix(self, edge, row, value): - edge_name = self.__convert_edge_to_name(edge) + edge_name = self.convert_edge_to_name(edge) m = self._connectivity_matrix column = self._connectivity_column_headers.index(edge_name) m[row, column] = value @@ -368,7 +379,7 @@ def add_to_column_skip_list(self, edge: Tuple[str, str]): # TODO - add the column to skip edge list to # prevent double count during xor finding edge_index = self._connectivity_column_headers.index( - self.__convert_edge_to_name(edge) + self.convert_edge_to_name(edge) ) self._or_column_skip_list.append(edge_index) diff --git a/lfr/graphmatch/interface.py b/lfr/graphmatch/interface.py index 9f5cbad..92e7e82 100644 --- a/lfr/graphmatch/interface.py +++ b/lfr/graphmatch/interface.py @@ -1,4 +1,3 @@ -from networkx.classes.digraph import DiGraph from lfr.graphmatch.figmappingmatcher import FIGMappingMatcher from typing import Dict from lfr.netlistgenerator.mappinglibrary import MappingLibrary From 504dc9391e8c8b2985e972bc8e12e602e1585600 Mon Sep 17 00:00:00 2001 From: Radhakrishna Sanka Date: Wed, 14 Apr 2021 17:38:33 -0400 Subject: [PATCH 12/36] Updated to reduce the problems --- lfr/compiler/distribute/distributeblock.py | 6 ++++-- lfr/compiler/distribute/statetable.py | 2 +- lfr/compiler/module.py | 1 + lfr/compiler/moduleio.py | 1 + 4 files changed, 7 insertions(+), 3 deletions(-) diff --git a/lfr/compiler/distribute/distributeblock.py b/lfr/compiler/distribute/distributeblock.py index dac0a1d..b62637e 100644 --- a/lfr/compiler/distribute/distributeblock.py +++ b/lfr/compiler/distribute/distributeblock.py @@ -2,6 +2,7 @@ from lfr.compiler.distribute.BitVector import BitVector from lfr.compiler.distribute.statetable import StateTable +from typing import List, Optional from lfr.compiler.language.vectorrange import VectorRange from lfr.fig.fluidinteractiongraph import FluidInteractionGraph @@ -10,7 +11,7 @@ class DistributeBlock: def __init__(self) -> None: self._sensitivity_list: List[VectorRange] = [] # self._state_header: List[str] = None - self._state_table: StateTable = None + self._state_table: Optional[StateTable] = None def generate_fig(self, fig: FluidInteractionGraph) -> None: # TODO - Create the fig based on the given distribute logic shown here @@ -37,6 +38,7 @@ def generate_fig(self, fig: FluidInteractionGraph) -> None: @property def state_table(self) -> StateTable: + assert self._state_table is not None return self._state_table @property @@ -89,7 +91,7 @@ def generate_state_vector( i += 1 ret = self._state_table.convert_to_fullstate_vector( - individual_signal_list, individual_value_list + signal_list=individual_signal_list, state=individual_value_list ) return ret diff --git a/lfr/compiler/distribute/statetable.py b/lfr/compiler/distribute/statetable.py index 4236427..931fd34 100644 --- a/lfr/compiler/distribute/statetable.py +++ b/lfr/compiler/distribute/statetable.py @@ -45,7 +45,7 @@ def get_connectivity_edge(self, i: int) -> Tuple[str, str]: return edge def convert_to_fullstate_vector( - self, signal_list: List[str], state: BitVector + self, signal_list: List[str], state: List[bool] ) -> BitVector: # Go through the each of the signal and update the specific BitVector value full_state_bitvector = BitVector(size=len(self._headers)) diff --git a/lfr/compiler/module.py b/lfr/compiler/module.py index 3c540e4..952a95f 100644 --- a/lfr/compiler/module.py +++ b/lfr/compiler/module.py @@ -79,6 +79,7 @@ def add_fluid_connection(self, item1id: str, item2id: str) -> None: def add_fluid_custom_interaction( self, item: Flow, operator: str, interaction_type: InteractionType ) -> Interaction: + # TODO - Figure out why the interaction_type is not being used here # Check if the item exists finteraction = FluidProcessInteraction(item, operator) self.FIG.add_interaction(finteraction) diff --git a/lfr/compiler/moduleio.py b/lfr/compiler/moduleio.py index d663330..378cc15 100644 --- a/lfr/compiler/moduleio.py +++ b/lfr/compiler/moduleio.py @@ -14,6 +14,7 @@ def id(self) -> str: @property def vector_ref(self) -> VectorRange: + assert self._vector_ref is not None return self._vector_ref @vector_ref.setter From 285e06a094022213e2595167b14dcb38bcb7400e Mon Sep 17 00:00:00 2001 From: Radhakrishna Sanka Date: Mon, 21 Jun 2021 16:54:43 -0400 Subject: [PATCH 13/36] Reduced more errors in the problems --- lfr/cmdline.py | 5 ----- lfr/netlistgenerator/dafdadapter.py | 14 ++++++++++---- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/lfr/cmdline.py b/lfr/cmdline.py index fe90788..ab54582 100644 --- a/lfr/cmdline.py +++ b/lfr/cmdline.py @@ -94,11 +94,6 @@ def main(): path = Path(parameters.OUTPUT_DIR) path.mkdir(parents=True) - library_name = args.technology - # libraries = load_libraries() - # if library_name not in libraries.keys(): - # raise Exception("Could not find mapping library") - library = None # library = libraries[library_name] diff --git a/lfr/netlistgenerator/dafdadapter.py b/lfr/netlistgenerator/dafdadapter.py index 0a6115a..e7cc240 100644 --- a/lfr/netlistgenerator/dafdadapter.py +++ b/lfr/netlistgenerator/dafdadapter.py @@ -1,8 +1,12 @@ -from typing import List, Optional - +from lfr.postprocessor.constraints import ( + ConstraintList, + FunctionalConstraint, + GeometryConstraint, + PerformanceConstraint, +) +from pymint.mintdevice import MINTDevice from dafd import DAFD_Interface from pymint.mintcomponent import MINTComponent -from pymint.mintdevice import MINTDevice class DAFDSizingAdapter: @@ -20,6 +24,7 @@ def size_droplet_generator(self, constriants: ConstraintList) -> None: if constraint.key == "volume": # r≈0.62035V1/3 volume = constraint.get_target_value() + assert volume is not None targets_dict["droplet_size"] = float(volume) ** 0.33 * 0.62035 * 2 elif isinstance(constraint, PerformanceConstraint): if constraint.key == "generation_rate": @@ -56,7 +61,8 @@ def size_droplet_generator(self, constriants: ConstraintList) -> None: component.params.set_param("outputLength", 5000) component.params.set_param("height", round(orifice_size / aspect_ratio)) - # TODO: Figure out how to propagate the results to the rest of the design. Additionally we need to set all the operation considtions + # TODO: Figure out how to propagate the results to the rest of the design. + # Additionally we need to set all the operation considtions pass def size_component(self, constriants: ConstraintList) -> None: From a050e49ac76b486e6d125f7a6e87e71ab93b76d1 Mon Sep 17 00:00:00 2001 From: Radhakrishna Sanka Date: Tue, 22 Jun 2021 15:28:27 -0400 Subject: [PATCH 14/36] Fixed a couple of things that should make this work more smoothly --- lfr/fig/fluidinteractiongraph.py | 12 ++++-------- lfr/netlistgenerator/v2/generator.py | 1 + 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/lfr/fig/fluidinteractiongraph.py b/lfr/fig/fluidinteractiongraph.py index c6d2328..d2b15ca 100644 --- a/lfr/fig/fluidinteractiongraph.py +++ b/lfr/fig/fluidinteractiongraph.py @@ -62,8 +62,7 @@ def get_fignode(self, id: str) -> FIGNode: return self._fignodes[id] else: raise Exception( - "Cannot find the node '{}' in the FluidInteractionGraph" - .format(id) + "Cannot find the node '{}' in the FluidInteractionGraph".format(id) ) def load_fignodes(self, fig_nodes: List[FIGNode]) -> None: @@ -106,15 +105,12 @@ def rename_annotations(self, rename_map: Dict[str, str]) -> None: def add_interaction(self, interaction: Interaction): if interaction.id not in self._fignodes.keys(): - self._fignodes[interaction.id] = interaction + self.add_fignode(interaction) else: raise Exception( "Interaction already present in the FIG: {0}".format(interaction.id) ) - # Initialize this for the interaction - self._annotations_reverse_map[interaction] = [] - if isinstance(interaction, FluidFluidInteraction): self.__add_fluid_fluid_interaction(interaction) @@ -389,7 +385,7 @@ def __add_fluid_number_interaction( # Create new Value node val_node = ValueNode(self.__get_val_node_id(), interaction.value) - self._fignodes[val_node.id] = val_node + self.add_fignode(val_node) self.add_node(interaction.id) self.add_edge(interaction.fluid.id, interaction.id) @@ -407,7 +403,7 @@ def __add_fluid_integer_interaction( # Create new Value node val_node = ValueNode(self.__get_val_node_id(), interaction.value) - self._fignodes[val_node.id] = val_node + self.add_fignode(val_node) self.add_node(interaction.id) self.add_edge(interaction.fluid.id, interaction.id) diff --git a/lfr/netlistgenerator/v2/generator.py b/lfr/netlistgenerator/v2/generator.py index 55f4a87..26fb330 100644 --- a/lfr/netlistgenerator/v2/generator.py +++ b/lfr/netlistgenerator/v2/generator.py @@ -965,6 +965,7 @@ def generate(module: Module, library: MappingLibrary) -> MINTDevice: # Now since all the mapping options are finalized Extract the netlist necessary construction_graph.construct_components(name_generator, cur_device) + # TODO - Rewrite this whole thing !!! construction_graph.construct_connections(name_generator, cur_device) # Finally join all the netlist pieces attached to the construction nodes From f339301cd01ee403c4be0c5cd3996b7ec99cba78 Mon Sep 17 00:00:00 2001 From: Radhakrishna Sanka Date: Tue, 22 Jun 2021 15:34:20 -0400 Subject: [PATCH 15/36] Updated the lfr antlrgen compiler generation location --- lfr/antlrgen/lfr/__init__.py | 0 lfr/antlrgen/{ => lfr}/lfrX.interp | 0 lfr/antlrgen/{ => lfr}/lfrX.tokens | 0 lfr/antlrgen/{ => lfr}/lfrXLexer.interp | 0 lfr/antlrgen/{ => lfr}/lfrXLexer.py | 0 lfr/antlrgen/{ => lfr}/lfrXLexer.tokens | 0 lfr/antlrgen/{ => lfr}/lfrXListener.py | 0 lfr/antlrgen/{ => lfr}/lfrXParser.py | 0 lfr/antlrgen/{ => lfr}/lfrXVisitor.py | 0 lfr/cmdline.py | 5 +++-- lfr/compiler/module.py | 2 +- lfr/distBlockListener.py | 11 +++++------ lfr/lfrbaseListener.py | 4 ++-- lfr/moduleinstanceListener.py | 2 +- lfr/postProcessListener.py | 2 +- lfr/preprocessor.py | 4 ++-- 16 files changed, 15 insertions(+), 15 deletions(-) create mode 100644 lfr/antlrgen/lfr/__init__.py rename lfr/antlrgen/{ => lfr}/lfrX.interp (100%) rename lfr/antlrgen/{ => lfr}/lfrX.tokens (100%) rename lfr/antlrgen/{ => lfr}/lfrXLexer.interp (100%) rename lfr/antlrgen/{ => lfr}/lfrXLexer.py (100%) rename lfr/antlrgen/{ => lfr}/lfrXLexer.tokens (100%) rename lfr/antlrgen/{ => lfr}/lfrXListener.py (100%) rename lfr/antlrgen/{ => lfr}/lfrXParser.py (100%) rename lfr/antlrgen/{ => lfr}/lfrXVisitor.py (100%) diff --git a/lfr/antlrgen/lfr/__init__.py b/lfr/antlrgen/lfr/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/lfr/antlrgen/lfrX.interp b/lfr/antlrgen/lfr/lfrX.interp similarity index 100% rename from lfr/antlrgen/lfrX.interp rename to lfr/antlrgen/lfr/lfrX.interp diff --git a/lfr/antlrgen/lfrX.tokens b/lfr/antlrgen/lfr/lfrX.tokens similarity index 100% rename from lfr/antlrgen/lfrX.tokens rename to lfr/antlrgen/lfr/lfrX.tokens diff --git a/lfr/antlrgen/lfrXLexer.interp b/lfr/antlrgen/lfr/lfrXLexer.interp similarity index 100% rename from lfr/antlrgen/lfrXLexer.interp rename to lfr/antlrgen/lfr/lfrXLexer.interp diff --git a/lfr/antlrgen/lfrXLexer.py b/lfr/antlrgen/lfr/lfrXLexer.py similarity index 100% rename from lfr/antlrgen/lfrXLexer.py rename to lfr/antlrgen/lfr/lfrXLexer.py diff --git a/lfr/antlrgen/lfrXLexer.tokens b/lfr/antlrgen/lfr/lfrXLexer.tokens similarity index 100% rename from lfr/antlrgen/lfrXLexer.tokens rename to lfr/antlrgen/lfr/lfrXLexer.tokens diff --git a/lfr/antlrgen/lfrXListener.py b/lfr/antlrgen/lfr/lfrXListener.py similarity index 100% rename from lfr/antlrgen/lfrXListener.py rename to lfr/antlrgen/lfr/lfrXListener.py diff --git a/lfr/antlrgen/lfrXParser.py b/lfr/antlrgen/lfr/lfrXParser.py similarity index 100% rename from lfr/antlrgen/lfrXParser.py rename to lfr/antlrgen/lfr/lfrXParser.py diff --git a/lfr/antlrgen/lfrXVisitor.py b/lfr/antlrgen/lfr/lfrXVisitor.py similarity index 100% rename from lfr/antlrgen/lfrXVisitor.py rename to lfr/antlrgen/lfr/lfrXVisitor.py diff --git a/lfr/cmdline.py b/lfr/cmdline.py index ab54582..e34977e 100644 --- a/lfr/cmdline.py +++ b/lfr/cmdline.py @@ -1,6 +1,7 @@ import argparse import glob import json +from lfr.postProcessListener import PostProcessListener import os import sys from pathlib import Path @@ -8,9 +9,9 @@ from antlr4 import CommonTokenStream, FileStream, ParseTreeWalker import lfr.parameters as parameters -from lfr.antlrgen.lfrXLexer import lfrXLexer -from lfr.antlrgen.lfrXParser import lfrXParser from lfr.moduleinstanceListener import ModuleInstanceListener +from lfr.antlrgen.lfr.lfrXLexer import lfrXLexer +from lfr.antlrgen.lfr.lfrXParser import lfrXParser from lfr.netlistgenerator.mappinglibrary import MappingLibrary from lfr.netlistgenerator.v2.generator import ( generate, diff --git a/lfr/compiler/module.py b/lfr/compiler/module.py index 952a95f..38299ba 100644 --- a/lfr/compiler/module.py +++ b/lfr/compiler/module.py @@ -27,7 +27,7 @@ def __init__(self, name): self.name = name self._imported_modules: List[Module] = [] self._io: List[ModuleIO] = [] - self.FIG = FluidInteractionGraph() + self.FIG: FluidInteractionGraph = FluidInteractionGraph() self.fluids = {} self._mappings: List[NodeMappingTemplate] = [] diff --git a/lfr/distBlockListener.py b/lfr/distBlockListener.py index 6782ebc..42f8e51 100644 --- a/lfr/distBlockListener.py +++ b/lfr/distBlockListener.py @@ -1,11 +1,10 @@ -from typing import List, Optional, Tuple - -from lfr.antlrgen.lfrXParser import lfrXParser -from lfr.compiler.distribute.BitVector import BitVector -from lfr.compiler.distribute.distributeblock import DistributeBlock -from lfr.compiler.language.vectorrange import VectorRange +from typing import List, Tuple, Optional from lfr.compiler.lfrerror import ErrorType, LFRError +from lfr.compiler.distribute.distributeblock import DistributeBlock +from lfr.antlrgen.lfr.lfrXParser import lfrXParser from lfr.lfrbaseListener import LFRBaseListener, ListenerMode +from lfr.compiler.language.vectorrange import VectorRange +from lfr.compiler.distribute.BitVector import BitVector class DistBlockListener(LFRBaseListener): diff --git a/lfr/lfrbaseListener.py b/lfr/lfrbaseListener.py index 9702d31..78c14de 100644 --- a/lfr/lfrbaseListener.py +++ b/lfr/lfrbaseListener.py @@ -2,8 +2,6 @@ from enum import Enum from typing import List, Optional -from lfr.antlrgen.lfrXListener import lfrXListener -from lfr.antlrgen.lfrXParser import lfrXParser from lfr.compiler.distribute.BitVector import BitVector from lfr.compiler.language.concatenation import Concatenation from lfr.compiler.language.fluidexpression import FluidExpression @@ -14,6 +12,8 @@ from lfr.compiler.module import Module from lfr.compiler.moduleio import ModuleIO from lfr.fig.fignode import Flow, IONode, IOType, Pump, Signal, Storage +from lfr.antlrgen.lfr.lfrXListener import lfrXListener +from lfr.antlrgen.lfr.lfrXParser import lfrXParser class ListenerMode(Enum): diff --git a/lfr/moduleinstanceListener.py b/lfr/moduleinstanceListener.py index 298f050..123a2e6 100644 --- a/lfr/moduleinstanceListener.py +++ b/lfr/moduleinstanceListener.py @@ -1,8 +1,8 @@ from typing import Dict, Optional -from lfr.antlrgen.lfrXParser import lfrXParser from lfr.compiler.lfrerror import ErrorType, LFRError from lfr.compiler.module import Module +from lfr.antlrgen.lfr.lfrXParser import lfrXParser from lfr.distBlockListener import DistBlockListener diff --git a/lfr/postProcessListener.py b/lfr/postProcessListener.py index 2a9a1df..3c9c4cf 100644 --- a/lfr/postProcessListener.py +++ b/lfr/postProcessListener.py @@ -1,6 +1,5 @@ from typing import Dict, List -from lfr.antlrgen.lfrXParser import lfrXParser from lfr.fig.fignode import FIGNode from lfr.fig.interaction import FluidProcessInteraction, Interaction from lfr.moduleinstanceListener import ModuleInstanceListener @@ -12,6 +11,7 @@ PumpMapping, StorageMapping, ) +from lfr.antlrgen.lfr.lfrXParser import lfrXParser class PostProcessListener(ModuleInstanceListener): diff --git a/lfr/preprocessor.py b/lfr/preprocessor.py index a700a75..d1baf4b 100644 --- a/lfr/preprocessor.py +++ b/lfr/preprocessor.py @@ -7,8 +7,8 @@ from antlr4.CommonTokenStream import CommonTokenStream from antlr4.FileStream import FileStream -from lfr.antlrgen.lfrXLexer import lfrXLexer -from lfr.antlrgen.lfrXParser import lfrXParser +from lfr.antlrgen.lfr.lfrXLexer import lfrXLexer +from lfr.antlrgen.lfr.lfrXParser import lfrXParser IMPORT_FILE_PATTERN = r"(`import\s+\"(\w+.lfr)\")" From 86008ddf9f6237bfaead141300baeace8588624f Mon Sep 17 00:00:00 2001 From: Radhakrishna Sanka Date: Tue, 22 Jun 2021 16:07:58 -0400 Subject: [PATCH 16/36] Moved reggie into lfr project folder --- LFR-Logo-01.png | Bin 0 -> 219362 bytes README.md | 12 +- lfr/antlrgen/reggie/reggie.interp | 65 + lfr/antlrgen/reggie/reggie.tokens | 33 + lfr/antlrgen/reggie/reggieLexer.interp | 78 ++ lfr/antlrgen/reggie/reggieLexer.py | 110 ++ lfr/antlrgen/reggie/reggieLexer.tokens | 33 + lfr/antlrgen/reggie/reggieListener.py | 145 +++ lfr/antlrgen/reggie/reggieParser.py | 1180 ++++++++++++++++++ lfr/antlrgen/reggie/reggieVisitor.py | 81 ++ lfr/graphmatch/README.md | 886 +++++++++++++ lfr/graphmatch/figmappingmatcher.py | 2 +- lfr/graphmatch/interface.py | 4 +- lfr/graphmatch/matchpattern.py | 59 + lfr/graphmatch/matchpatterngenerator.py | 57 + lfr/graphmatch/nodefilter.py | 27 + lfr/netlistgenerator/v2/constructiongraph.py | 1 + reggie.g4 | 63 + 18 files changed, 2831 insertions(+), 5 deletions(-) create mode 100644 LFR-Logo-01.png create mode 100644 lfr/antlrgen/reggie/reggie.interp create mode 100644 lfr/antlrgen/reggie/reggie.tokens create mode 100644 lfr/antlrgen/reggie/reggieLexer.interp create mode 100644 lfr/antlrgen/reggie/reggieLexer.py create mode 100644 lfr/antlrgen/reggie/reggieLexer.tokens create mode 100644 lfr/antlrgen/reggie/reggieListener.py create mode 100644 lfr/antlrgen/reggie/reggieParser.py create mode 100644 lfr/antlrgen/reggie/reggieVisitor.py create mode 100644 lfr/graphmatch/README.md create mode 100644 lfr/graphmatch/matchpattern.py create mode 100644 lfr/graphmatch/matchpatterngenerator.py create mode 100644 lfr/graphmatch/nodefilter.py create mode 100644 reggie.g4 diff --git a/LFR-Logo-01.png b/LFR-Logo-01.png new file mode 100644 index 0000000000000000000000000000000000000000..3254a8026838d604546990df8ada050fdab78d8d GIT binary patch literal 219362 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

L8Ze>K9%Mud!IK5epr5M54*_j^acBTH^rrzWE- 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 literal 0 HcmV?d00001 diff --git a/README.md b/README.md index f48f598..0a21a4d 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# LFR +# LFR ## Dependencies @@ -8,8 +8,16 @@ LFR requires the user to install graphviz onto the system for the pygraphviz dep We need ANTLR for generating the listener. Install ANTLR4 from the [website](https://www.antlr.org/index.html) +### LFR Grammar + +``` +antlr4 -o ./lfr/antlrgen/lfr -listener -visitor -Dlanguage=Python3 -lib . ./lfrX.g4 +``` + +### Reggie(Graph Match) Grammar + ``` -antlr4 -o ./lfr/antlrgen -listener -visitor -Dlanguage=Python3 -lib . ./lfrX.g4 +antlr4 -o ./lfr/antlrgen/reggie -listener -visitor -Dlanguage=Python3 -lib . ./reggie.g4 ``` ### Graphviz diff --git a/lfr/antlrgen/reggie/reggie.interp b/lfr/antlrgen/reggie/reggie.interp new file mode 100644 index 0000000..125373a --- /dev/null +++ b/lfr/antlrgen/reggie/reggie.interp @@ -0,0 +1,65 @@ +token literal names: +null +'{' +',' +'}' +'(' +')' +':' +'[' +']' +'*' +'+' +'|' +'->' +'<->' +null +'?' +null +null +null +null + +token symbolic names: +null +null +null +null +null +null +null +null +null +null +null +null +null +null +ID +QUESTIONMARK_WILDCARD +WS +NL +INT +STRING + +rule names: +graph +graphstatement +statementmodifier +basestatement +subgraph +vertex +coloringfilter +structuralvertexpattern +intmodifier +starmodifier +plusmodifier +structuralid +labelfilter +label +vertex2vertex +edge + + +atn: +[3, 24715, 42794, 33075, 47597, 16764, 15335, 30598, 22884, 3, 21, 152, 4, 2, 9, 2, 4, 3, 9, 3, 4, 4, 9, 4, 4, 5, 9, 5, 4, 6, 9, 6, 4, 7, 9, 7, 4, 8, 9, 8, 4, 9, 9, 9, 4, 10, 9, 10, 4, 11, 9, 11, 4, 12, 9, 12, 4, 13, 9, 13, 4, 14, 9, 14, 4, 15, 9, 15, 4, 16, 9, 16, 4, 17, 9, 17, 3, 2, 3, 2, 3, 2, 3, 2, 7, 2, 39, 10, 2, 12, 2, 14, 2, 42, 11, 2, 3, 2, 3, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 5, 3, 52, 10, 3, 3, 4, 3, 4, 3, 4, 5, 4, 57, 10, 4, 3, 5, 3, 5, 3, 5, 5, 5, 62, 10, 5, 3, 6, 3, 6, 3, 6, 3, 6, 3, 6, 3, 6, 3, 6, 7, 6, 71, 10, 6, 12, 6, 14, 6, 74, 11, 6, 3, 6, 3, 6, 3, 7, 3, 7, 3, 7, 5, 7, 81, 10, 7, 3, 7, 5, 7, 84, 10, 7, 3, 7, 7, 7, 87, 10, 7, 12, 7, 14, 7, 90, 11, 7, 3, 8, 3, 8, 3, 8, 3, 8, 7, 8, 96, 10, 8, 12, 8, 14, 8, 99, 11, 8, 3, 8, 3, 8, 3, 9, 3, 9, 3, 9, 5, 9, 106, 10, 9, 3, 10, 3, 10, 3, 10, 3, 10, 3, 11, 3, 11, 3, 12, 3, 12, 3, 13, 3, 13, 3, 14, 3, 14, 3, 14, 7, 14, 121, 10, 14, 12, 14, 14, 14, 124, 11, 14, 3, 14, 3, 14, 3, 14, 3, 14, 7, 14, 130, 10, 14, 12, 14, 14, 14, 133, 11, 14, 3, 14, 3, 14, 5, 14, 137, 10, 14, 3, 15, 3, 15, 3, 16, 3, 16, 3, 16, 3, 16, 7, 16, 145, 10, 16, 12, 16, 14, 16, 148, 11, 16, 3, 17, 3, 17, 3, 17, 2, 2, 18, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 2, 4, 3, 2, 16, 17, 3, 2, 14, 15, 2, 152, 2, 34, 3, 2, 2, 2, 4, 51, 3, 2, 2, 2, 6, 56, 3, 2, 2, 2, 8, 61, 3, 2, 2, 2, 10, 63, 3, 2, 2, 2, 12, 77, 3, 2, 2, 2, 14, 91, 3, 2, 2, 2, 16, 105, 3, 2, 2, 2, 18, 107, 3, 2, 2, 2, 20, 111, 3, 2, 2, 2, 22, 113, 3, 2, 2, 2, 24, 115, 3, 2, 2, 2, 26, 136, 3, 2, 2, 2, 28, 138, 3, 2, 2, 2, 30, 140, 3, 2, 2, 2, 32, 149, 3, 2, 2, 2, 34, 35, 7, 3, 2, 2, 35, 40, 5, 4, 3, 2, 36, 37, 7, 4, 2, 2, 37, 39, 5, 4, 3, 2, 38, 36, 3, 2, 2, 2, 39, 42, 3, 2, 2, 2, 40, 38, 3, 2, 2, 2, 40, 41, 3, 2, 2, 2, 41, 43, 3, 2, 2, 2, 42, 40, 3, 2, 2, 2, 43, 44, 7, 5, 2, 2, 44, 3, 3, 2, 2, 2, 45, 52, 5, 8, 5, 2, 46, 47, 7, 6, 2, 2, 47, 48, 5, 8, 5, 2, 48, 49, 7, 7, 2, 2, 49, 50, 5, 6, 4, 2, 50, 52, 3, 2, 2, 2, 51, 45, 3, 2, 2, 2, 51, 46, 3, 2, 2, 2, 52, 5, 3, 2, 2, 2, 53, 57, 5, 18, 10, 2, 54, 57, 5, 22, 12, 2, 55, 57, 5, 20, 11, 2, 56, 53, 3, 2, 2, 2, 56, 54, 3, 2, 2, 2, 56, 55, 3, 2, 2, 2, 57, 7, 3, 2, 2, 2, 58, 62, 5, 10, 6, 2, 59, 62, 5, 30, 16, 2, 60, 62, 5, 12, 7, 2, 61, 58, 3, 2, 2, 2, 61, 59, 3, 2, 2, 2, 61, 60, 3, 2, 2, 2, 62, 9, 3, 2, 2, 2, 63, 64, 7, 16, 2, 2, 64, 65, 7, 8, 2, 2, 65, 66, 3, 2, 2, 2, 66, 67, 7, 3, 2, 2, 67, 72, 5, 30, 16, 2, 68, 69, 7, 4, 2, 2, 69, 71, 5, 30, 16, 2, 70, 68, 3, 2, 2, 2, 71, 74, 3, 2, 2, 2, 72, 70, 3, 2, 2, 2, 72, 73, 3, 2, 2, 2, 73, 75, 3, 2, 2, 2, 74, 72, 3, 2, 2, 2, 75, 76, 7, 5, 2, 2, 76, 11, 3, 2, 2, 2, 77, 80, 5, 24, 13, 2, 78, 79, 7, 8, 2, 2, 79, 81, 5, 26, 14, 2, 80, 78, 3, 2, 2, 2, 80, 81, 3, 2, 2, 2, 81, 83, 3, 2, 2, 2, 82, 84, 5, 14, 8, 2, 83, 82, 3, 2, 2, 2, 83, 84, 3, 2, 2, 2, 84, 88, 3, 2, 2, 2, 85, 87, 5, 16, 9, 2, 86, 85, 3, 2, 2, 2, 87, 90, 3, 2, 2, 2, 88, 86, 3, 2, 2, 2, 88, 89, 3, 2, 2, 2, 89, 13, 3, 2, 2, 2, 90, 88, 3, 2, 2, 2, 91, 92, 7, 3, 2, 2, 92, 97, 7, 21, 2, 2, 93, 94, 7, 4, 2, 2, 94, 96, 7, 21, 2, 2, 95, 93, 3, 2, 2, 2, 96, 99, 3, 2, 2, 2, 97, 95, 3, 2, 2, 2, 97, 98, 3, 2, 2, 2, 98, 100, 3, 2, 2, 2, 99, 97, 3, 2, 2, 2, 100, 101, 7, 5, 2, 2, 101, 15, 3, 2, 2, 2, 102, 106, 5, 18, 10, 2, 103, 106, 5, 20, 11, 2, 104, 106, 5, 22, 12, 2, 105, 102, 3, 2, 2, 2, 105, 103, 3, 2, 2, 2, 105, 104, 3, 2, 2, 2, 106, 17, 3, 2, 2, 2, 107, 108, 7, 9, 2, 2, 108, 109, 7, 20, 2, 2, 109, 110, 7, 10, 2, 2, 110, 19, 3, 2, 2, 2, 111, 112, 7, 11, 2, 2, 112, 21, 3, 2, 2, 2, 113, 114, 7, 12, 2, 2, 114, 23, 3, 2, 2, 2, 115, 116, 9, 2, 2, 2, 116, 25, 3, 2, 2, 2, 117, 122, 5, 28, 15, 2, 118, 119, 7, 13, 2, 2, 119, 121, 5, 28, 15, 2, 120, 118, 3, 2, 2, 2, 121, 124, 3, 2, 2, 2, 122, 120, 3, 2, 2, 2, 122, 123, 3, 2, 2, 2, 123, 137, 3, 2, 2, 2, 124, 122, 3, 2, 2, 2, 125, 126, 7, 6, 2, 2, 126, 131, 5, 28, 15, 2, 127, 128, 7, 13, 2, 2, 128, 130, 5, 28, 15, 2, 129, 127, 3, 2, 2, 2, 130, 133, 3, 2, 2, 2, 131, 129, 3, 2, 2, 2, 131, 132, 3, 2, 2, 2, 132, 134, 3, 2, 2, 2, 133, 131, 3, 2, 2, 2, 134, 135, 7, 7, 2, 2, 135, 137, 3, 2, 2, 2, 136, 117, 3, 2, 2, 2, 136, 125, 3, 2, 2, 2, 137, 27, 3, 2, 2, 2, 138, 139, 7, 16, 2, 2, 139, 29, 3, 2, 2, 2, 140, 146, 5, 12, 7, 2, 141, 142, 5, 32, 17, 2, 142, 143, 5, 12, 7, 2, 143, 145, 3, 2, 2, 2, 144, 141, 3, 2, 2, 2, 145, 148, 3, 2, 2, 2, 146, 144, 3, 2, 2, 2, 146, 147, 3, 2, 2, 2, 147, 31, 3, 2, 2, 2, 148, 146, 3, 2, 2, 2, 149, 150, 9, 3, 2, 2, 150, 33, 3, 2, 2, 2, 16, 40, 51, 56, 61, 72, 80, 83, 88, 97, 105, 122, 131, 136, 146] \ No newline at end of file diff --git a/lfr/antlrgen/reggie/reggie.tokens b/lfr/antlrgen/reggie/reggie.tokens new file mode 100644 index 0000000..dd59d83 --- /dev/null +++ b/lfr/antlrgen/reggie/reggie.tokens @@ -0,0 +1,33 @@ +T__0=1 +T__1=2 +T__2=3 +T__3=4 +T__4=5 +T__5=6 +T__6=7 +T__7=8 +T__8=9 +T__9=10 +T__10=11 +T__11=12 +T__12=13 +ID=14 +QUESTIONMARK_WILDCARD=15 +WS=16 +NL=17 +INT=18 +STRING=19 +'{'=1 +','=2 +'}'=3 +'('=4 +')'=5 +':'=6 +'['=7 +']'=8 +'*'=9 +'+'=10 +'|'=11 +'->'=12 +'<->'=13 +'?'=15 diff --git a/lfr/antlrgen/reggie/reggieLexer.interp b/lfr/antlrgen/reggie/reggieLexer.interp new file mode 100644 index 0000000..e00acfc --- /dev/null +++ b/lfr/antlrgen/reggie/reggieLexer.interp @@ -0,0 +1,78 @@ +token literal names: +null +'{' +',' +'}' +'(' +')' +':' +'[' +']' +'*' +'+' +'|' +'->' +'<->' +null +'?' +null +null +null +null + +token symbolic names: +null +null +null +null +null +null +null +null +null +null +null +null +null +null +ID +QUESTIONMARK_WILDCARD +WS +NL +INT +STRING + +rule names: +T__0 +T__1 +T__2 +T__3 +T__4 +T__5 +T__6 +T__7 +T__8 +T__9 +T__10 +T__11 +T__12 +ID +QUESTIONMARK_WILDCARD +WS +NL +INT +STRING +HEX +UNICODE +ESC +SAFECODEPOINT + +channel names: +DEFAULT_TOKEN_CHANNEL +HIDDEN + +mode names: +DEFAULT_MODE + +atn: +[3, 24715, 42794, 33075, 47597, 16764, 15335, 30598, 22884, 2, 21, 131, 8, 1, 4, 2, 9, 2, 4, 3, 9, 3, 4, 4, 9, 4, 4, 5, 9, 5, 4, 6, 9, 6, 4, 7, 9, 7, 4, 8, 9, 8, 4, 9, 9, 9, 4, 10, 9, 10, 4, 11, 9, 11, 4, 12, 9, 12, 4, 13, 9, 13, 4, 14, 9, 14, 4, 15, 9, 15, 4, 16, 9, 16, 4, 17, 9, 17, 4, 18, 9, 18, 4, 19, 9, 19, 4, 20, 9, 20, 4, 21, 9, 21, 4, 22, 9, 22, 4, 23, 9, 23, 4, 24, 9, 24, 3, 2, 3, 2, 3, 3, 3, 3, 3, 4, 3, 4, 3, 5, 3, 5, 3, 6, 3, 6, 3, 7, 3, 7, 3, 8, 3, 8, 3, 9, 3, 9, 3, 10, 3, 10, 3, 11, 3, 11, 3, 12, 3, 12, 3, 13, 3, 13, 3, 13, 3, 14, 3, 14, 3, 14, 3, 14, 3, 15, 3, 15, 7, 15, 81, 10, 15, 12, 15, 14, 15, 84, 11, 15, 3, 16, 3, 16, 3, 17, 6, 17, 89, 10, 17, 13, 17, 14, 17, 90, 3, 17, 3, 17, 3, 18, 6, 18, 96, 10, 18, 13, 18, 14, 18, 97, 3, 18, 3, 18, 3, 19, 6, 19, 103, 10, 19, 13, 19, 14, 19, 104, 3, 20, 3, 20, 3, 20, 7, 20, 110, 10, 20, 12, 20, 14, 20, 113, 11, 20, 3, 20, 3, 20, 3, 21, 3, 21, 3, 22, 3, 22, 3, 22, 3, 22, 3, 22, 3, 22, 3, 23, 3, 23, 3, 23, 5, 23, 128, 10, 23, 3, 24, 3, 24, 2, 2, 25, 3, 3, 5, 4, 7, 5, 9, 6, 11, 7, 13, 8, 15, 9, 17, 10, 19, 11, 21, 12, 23, 13, 25, 14, 27, 15, 29, 16, 31, 17, 33, 18, 35, 19, 37, 20, 39, 21, 41, 2, 43, 2, 45, 2, 47, 2, 3, 2, 10, 5, 2, 67, 92, 97, 97, 99, 124, 6, 2, 50, 59, 67, 92, 97, 97, 99, 124, 4, 2, 11, 11, 34, 34, 4, 2, 12, 12, 15, 15, 3, 2, 50, 59, 5, 2, 50, 59, 67, 72, 99, 104, 10, 2, 36, 36, 49, 49, 94, 94, 100, 100, 104, 104, 112, 112, 116, 116, 118, 118, 5, 2, 2, 33, 36, 36, 94, 94, 2, 133, 2, 3, 3, 2, 2, 2, 2, 5, 3, 2, 2, 2, 2, 7, 3, 2, 2, 2, 2, 9, 3, 2, 2, 2, 2, 11, 3, 2, 2, 2, 2, 13, 3, 2, 2, 2, 2, 15, 3, 2, 2, 2, 2, 17, 3, 2, 2, 2, 2, 19, 3, 2, 2, 2, 2, 21, 3, 2, 2, 2, 2, 23, 3, 2, 2, 2, 2, 25, 3, 2, 2, 2, 2, 27, 3, 2, 2, 2, 2, 29, 3, 2, 2, 2, 2, 31, 3, 2, 2, 2, 2, 33, 3, 2, 2, 2, 2, 35, 3, 2, 2, 2, 2, 37, 3, 2, 2, 2, 2, 39, 3, 2, 2, 2, 3, 49, 3, 2, 2, 2, 5, 51, 3, 2, 2, 2, 7, 53, 3, 2, 2, 2, 9, 55, 3, 2, 2, 2, 11, 57, 3, 2, 2, 2, 13, 59, 3, 2, 2, 2, 15, 61, 3, 2, 2, 2, 17, 63, 3, 2, 2, 2, 19, 65, 3, 2, 2, 2, 21, 67, 3, 2, 2, 2, 23, 69, 3, 2, 2, 2, 25, 71, 3, 2, 2, 2, 27, 74, 3, 2, 2, 2, 29, 78, 3, 2, 2, 2, 31, 85, 3, 2, 2, 2, 33, 88, 3, 2, 2, 2, 35, 95, 3, 2, 2, 2, 37, 102, 3, 2, 2, 2, 39, 106, 3, 2, 2, 2, 41, 116, 3, 2, 2, 2, 43, 118, 3, 2, 2, 2, 45, 124, 3, 2, 2, 2, 47, 129, 3, 2, 2, 2, 49, 50, 7, 125, 2, 2, 50, 4, 3, 2, 2, 2, 51, 52, 7, 46, 2, 2, 52, 6, 3, 2, 2, 2, 53, 54, 7, 127, 2, 2, 54, 8, 3, 2, 2, 2, 55, 56, 7, 42, 2, 2, 56, 10, 3, 2, 2, 2, 57, 58, 7, 43, 2, 2, 58, 12, 3, 2, 2, 2, 59, 60, 7, 60, 2, 2, 60, 14, 3, 2, 2, 2, 61, 62, 7, 93, 2, 2, 62, 16, 3, 2, 2, 2, 63, 64, 7, 95, 2, 2, 64, 18, 3, 2, 2, 2, 65, 66, 7, 44, 2, 2, 66, 20, 3, 2, 2, 2, 67, 68, 7, 45, 2, 2, 68, 22, 3, 2, 2, 2, 69, 70, 7, 126, 2, 2, 70, 24, 3, 2, 2, 2, 71, 72, 7, 47, 2, 2, 72, 73, 7, 64, 2, 2, 73, 26, 3, 2, 2, 2, 74, 75, 7, 62, 2, 2, 75, 76, 7, 47, 2, 2, 76, 77, 7, 64, 2, 2, 77, 28, 3, 2, 2, 2, 78, 82, 9, 2, 2, 2, 79, 81, 9, 3, 2, 2, 80, 79, 3, 2, 2, 2, 81, 84, 3, 2, 2, 2, 82, 80, 3, 2, 2, 2, 82, 83, 3, 2, 2, 2, 83, 30, 3, 2, 2, 2, 84, 82, 3, 2, 2, 2, 85, 86, 7, 65, 2, 2, 86, 32, 3, 2, 2, 2, 87, 89, 9, 4, 2, 2, 88, 87, 3, 2, 2, 2, 89, 90, 3, 2, 2, 2, 90, 88, 3, 2, 2, 2, 90, 91, 3, 2, 2, 2, 91, 92, 3, 2, 2, 2, 92, 93, 8, 17, 2, 2, 93, 34, 3, 2, 2, 2, 94, 96, 9, 5, 2, 2, 95, 94, 3, 2, 2, 2, 96, 97, 3, 2, 2, 2, 97, 95, 3, 2, 2, 2, 97, 98, 3, 2, 2, 2, 98, 99, 3, 2, 2, 2, 99, 100, 8, 18, 2, 2, 100, 36, 3, 2, 2, 2, 101, 103, 9, 6, 2, 2, 102, 101, 3, 2, 2, 2, 103, 104, 3, 2, 2, 2, 104, 102, 3, 2, 2, 2, 104, 105, 3, 2, 2, 2, 105, 38, 3, 2, 2, 2, 106, 111, 7, 36, 2, 2, 107, 110, 5, 45, 23, 2, 108, 110, 5, 47, 24, 2, 109, 107, 3, 2, 2, 2, 109, 108, 3, 2, 2, 2, 110, 113, 3, 2, 2, 2, 111, 109, 3, 2, 2, 2, 111, 112, 3, 2, 2, 2, 112, 114, 3, 2, 2, 2, 113, 111, 3, 2, 2, 2, 114, 115, 7, 36, 2, 2, 115, 40, 3, 2, 2, 2, 116, 117, 9, 7, 2, 2, 117, 42, 3, 2, 2, 2, 118, 119, 7, 119, 2, 2, 119, 120, 5, 41, 21, 2, 120, 121, 5, 41, 21, 2, 121, 122, 5, 41, 21, 2, 122, 123, 5, 41, 21, 2, 123, 44, 3, 2, 2, 2, 124, 127, 7, 94, 2, 2, 125, 128, 9, 8, 2, 2, 126, 128, 5, 43, 22, 2, 127, 125, 3, 2, 2, 2, 127, 126, 3, 2, 2, 2, 128, 46, 3, 2, 2, 2, 129, 130, 10, 9, 2, 2, 130, 48, 3, 2, 2, 2, 10, 2, 82, 90, 97, 104, 109, 111, 127, 3, 8, 2, 2] \ No newline at end of file diff --git a/lfr/antlrgen/reggie/reggieLexer.py b/lfr/antlrgen/reggie/reggieLexer.py new file mode 100644 index 0000000..c6cf007 --- /dev/null +++ b/lfr/antlrgen/reggie/reggieLexer.py @@ -0,0 +1,110 @@ +# Generated from /Users/krishna/CIDAR/reggie/reggie.g4 by ANTLR 4.9 +from antlr4 import * +from io import StringIO +from typing.io import TextIO +import sys + + + +def serializedATN(): + with StringIO() as buf: + buf.write("\3\u608b\ua72a\u8133\ub9ed\u417c\u3be7\u7786\u5964\2\25") + buf.write("\u0083\b\1\4\2\t\2\4\3\t\3\4\4\t\4\4\5\t\5\4\6\t\6\4\7") + buf.write("\t\7\4\b\t\b\4\t\t\t\4\n\t\n\4\13\t\13\4\f\t\f\4\r\t\r") + buf.write("\4\16\t\16\4\17\t\17\4\20\t\20\4\21\t\21\4\22\t\22\4\23") + buf.write("\t\23\4\24\t\24\4\25\t\25\4\26\t\26\4\27\t\27\4\30\t\30") + buf.write("\3\2\3\2\3\3\3\3\3\4\3\4\3\5\3\5\3\6\3\6\3\7\3\7\3\b\3") + buf.write("\b\3\t\3\t\3\n\3\n\3\13\3\13\3\f\3\f\3\r\3\r\3\r\3\16") + buf.write("\3\16\3\16\3\16\3\17\3\17\7\17Q\n\17\f\17\16\17T\13\17") + buf.write("\3\20\3\20\3\21\6\21Y\n\21\r\21\16\21Z\3\21\3\21\3\22") + buf.write("\6\22`\n\22\r\22\16\22a\3\22\3\22\3\23\6\23g\n\23\r\23") + buf.write("\16\23h\3\24\3\24\3\24\7\24n\n\24\f\24\16\24q\13\24\3") + buf.write("\24\3\24\3\25\3\25\3\26\3\26\3\26\3\26\3\26\3\26\3\27") + buf.write("\3\27\3\27\5\27\u0080\n\27\3\30\3\30\2\2\31\3\3\5\4\7") + buf.write("\5\t\6\13\7\r\b\17\t\21\n\23\13\25\f\27\r\31\16\33\17") + buf.write("\35\20\37\21!\22#\23%\24\'\25)\2+\2-\2/\2\3\2\n\5\2C\\") + buf.write("aac|\6\2\62;C\\aac|\4\2\13\13\"\"\4\2\f\f\17\17\3\2\62") + buf.write(";\5\2\62;CHch\n\2$$\61\61^^ddhhppttvv\5\2\2!$$^^\2\u0085") + buf.write("\2\3\3\2\2\2\2\5\3\2\2\2\2\7\3\2\2\2\2\t\3\2\2\2\2\13") + buf.write("\3\2\2\2\2\r\3\2\2\2\2\17\3\2\2\2\2\21\3\2\2\2\2\23\3") + buf.write("\2\2\2\2\25\3\2\2\2\2\27\3\2\2\2\2\31\3\2\2\2\2\33\3\2") + buf.write("\2\2\2\35\3\2\2\2\2\37\3\2\2\2\2!\3\2\2\2\2#\3\2\2\2\2") + buf.write("%\3\2\2\2\2\'\3\2\2\2\3\61\3\2\2\2\5\63\3\2\2\2\7\65\3") + buf.write("\2\2\2\t\67\3\2\2\2\139\3\2\2\2\r;\3\2\2\2\17=\3\2\2\2") + buf.write("\21?\3\2\2\2\23A\3\2\2\2\25C\3\2\2\2\27E\3\2\2\2\31G\3") + buf.write("\2\2\2\33J\3\2\2\2\35N\3\2\2\2\37U\3\2\2\2!X\3\2\2\2#") + buf.write("_\3\2\2\2%f\3\2\2\2\'j\3\2\2\2)t\3\2\2\2+v\3\2\2\2-|\3") + buf.write("\2\2\2/\u0081\3\2\2\2\61\62\7}\2\2\62\4\3\2\2\2\63\64") + buf.write("\7.\2\2\64\6\3\2\2\2\65\66\7\177\2\2\66\b\3\2\2\2\678") + buf.write("\7*\2\28\n\3\2\2\29:\7+\2\2:\f\3\2\2\2;<\7<\2\2<\16\3") + buf.write("\2\2\2=>\7]\2\2>\20\3\2\2\2?@\7_\2\2@\22\3\2\2\2AB\7,") + buf.write("\2\2B\24\3\2\2\2CD\7-\2\2D\26\3\2\2\2EF\7~\2\2F\30\3\2") + buf.write("\2\2GH\7/\2\2HI\7@\2\2I\32\3\2\2\2JK\7>\2\2KL\7/\2\2L") + buf.write("M\7@\2\2M\34\3\2\2\2NR\t\2\2\2OQ\t\3\2\2PO\3\2\2\2QT\3") + buf.write("\2\2\2RP\3\2\2\2RS\3\2\2\2S\36\3\2\2\2TR\3\2\2\2UV\7A") + buf.write("\2\2V \3\2\2\2WY\t\4\2\2XW\3\2\2\2YZ\3\2\2\2ZX\3\2\2\2") + buf.write("Z[\3\2\2\2[\\\3\2\2\2\\]\b\21\2\2]\"\3\2\2\2^`\t\5\2\2") + buf.write("_^\3\2\2\2`a\3\2\2\2a_\3\2\2\2ab\3\2\2\2bc\3\2\2\2cd\b") + buf.write("\22\2\2d$\3\2\2\2eg\t\6\2\2fe\3\2\2\2gh\3\2\2\2hf\3\2") + buf.write("\2\2hi\3\2\2\2i&\3\2\2\2jo\7$\2\2kn\5-\27\2ln\5/\30\2") + buf.write("mk\3\2\2\2ml\3\2\2\2nq\3\2\2\2om\3\2\2\2op\3\2\2\2pr\3") + buf.write("\2\2\2qo\3\2\2\2rs\7$\2\2s(\3\2\2\2tu\t\7\2\2u*\3\2\2") + buf.write("\2vw\7w\2\2wx\5)\25\2xy\5)\25\2yz\5)\25\2z{\5)\25\2{,") + buf.write("\3\2\2\2|\177\7^\2\2}\u0080\t\b\2\2~\u0080\5+\26\2\177") + buf.write("}\3\2\2\2\177~\3\2\2\2\u0080.\3\2\2\2\u0081\u0082\n\t") + buf.write("\2\2\u0082\60\3\2\2\2\n\2RZahmo\177\3\b\2\2") + return buf.getvalue() + + +class reggieLexer(Lexer): + + atn = ATNDeserializer().deserialize(serializedATN()) + + decisionsToDFA = [ DFA(ds, i) for i, ds in enumerate(atn.decisionToState) ] + + T__0 = 1 + T__1 = 2 + T__2 = 3 + T__3 = 4 + T__4 = 5 + T__5 = 6 + T__6 = 7 + T__7 = 8 + T__8 = 9 + T__9 = 10 + T__10 = 11 + T__11 = 12 + T__12 = 13 + ID = 14 + QUESTIONMARK_WILDCARD = 15 + WS = 16 + NL = 17 + INT = 18 + STRING = 19 + + channelNames = [ u"DEFAULT_TOKEN_CHANNEL", u"HIDDEN" ] + + modeNames = [ "DEFAULT_MODE" ] + + literalNames = [ "", + "'{'", "','", "'}'", "'('", "')'", "':'", "'['", "']'", "'*'", + "'+'", "'|'", "'->'", "'<->'", "'?'" ] + + symbolicNames = [ "", + "ID", "QUESTIONMARK_WILDCARD", "WS", "NL", "INT", "STRING" ] + + ruleNames = [ "T__0", "T__1", "T__2", "T__3", "T__4", "T__5", "T__6", + "T__7", "T__8", "T__9", "T__10", "T__11", "T__12", "ID", + "QUESTIONMARK_WILDCARD", "WS", "NL", "INT", "STRING", + "HEX", "UNICODE", "ESC", "SAFECODEPOINT" ] + + grammarFileName = "reggie.g4" + + def __init__(self, input=None, output:TextIO = sys.stdout): + super().__init__(input, output) + self.checkVersion("4.9") + self._interp = LexerATNSimulator(self, self.atn, self.decisionsToDFA, PredictionContextCache()) + self._actions = None + self._predicates = None + + diff --git a/lfr/antlrgen/reggie/reggieLexer.tokens b/lfr/antlrgen/reggie/reggieLexer.tokens new file mode 100644 index 0000000..dd59d83 --- /dev/null +++ b/lfr/antlrgen/reggie/reggieLexer.tokens @@ -0,0 +1,33 @@ +T__0=1 +T__1=2 +T__2=3 +T__3=4 +T__4=5 +T__5=6 +T__6=7 +T__7=8 +T__8=9 +T__9=10 +T__10=11 +T__11=12 +T__12=13 +ID=14 +QUESTIONMARK_WILDCARD=15 +WS=16 +NL=17 +INT=18 +STRING=19 +'{'=1 +','=2 +'}'=3 +'('=4 +')'=5 +':'=6 +'['=7 +']'=8 +'*'=9 +'+'=10 +'|'=11 +'->'=12 +'<->'=13 +'?'=15 diff --git a/lfr/antlrgen/reggie/reggieListener.py b/lfr/antlrgen/reggie/reggieListener.py new file mode 100644 index 0000000..8728cc6 --- /dev/null +++ b/lfr/antlrgen/reggie/reggieListener.py @@ -0,0 +1,145 @@ +# Generated from /Users/krishna/CIDAR/reggie/reggie.g4 by ANTLR 4.9 +from antlr4 import * + +if __name__ is not None and "." in __name__: + from .reggieParser import reggieParser +else: + from lfr.graphmatchParser import reggieParser + +# This class defines a complete listener for a parse tree produced by reggieParser. +class reggieListener(ParseTreeListener): + + # Enter a parse tree produced by reggieParser#graph. + def enterGraph(self, ctx: reggieParser.GraphContext): + pass + + # Exit a parse tree produced by reggieParser#graph. + def exitGraph(self, ctx: reggieParser.GraphContext): + pass + + # Enter a parse tree produced by reggieParser#graphstatement. + def enterGraphstatement(self, ctx: reggieParser.GraphstatementContext): + pass + + # Exit a parse tree produced by reggieParser#graphstatement. + def exitGraphstatement(self, ctx: reggieParser.GraphstatementContext): + pass + + # Enter a parse tree produced by reggieParser#statementmodifier. + def enterStatementmodifier(self, ctx: reggieParser.StatementmodifierContext): + pass + + # Exit a parse tree produced by reggieParser#statementmodifier. + def exitStatementmodifier(self, ctx: reggieParser.StatementmodifierContext): + pass + + # Enter a parse tree produced by reggieParser#basestatement. + def enterBasestatement(self, ctx: reggieParser.BasestatementContext): + pass + + # Exit a parse tree produced by reggieParser#basestatement. + def exitBasestatement(self, ctx: reggieParser.BasestatementContext): + pass + + # Enter a parse tree produced by reggieParser#subgraph. + def enterSubgraph(self, ctx: reggieParser.SubgraphContext): + pass + + # Exit a parse tree produced by reggieParser#subgraph. + def exitSubgraph(self, ctx: reggieParser.SubgraphContext): + pass + + # Enter a parse tree produced by reggieParser#vertex. + def enterVertex(self, ctx: reggieParser.VertexContext): + pass + + # Exit a parse tree produced by reggieParser#vertex. + def exitVertex(self, ctx: reggieParser.VertexContext): + pass + + # Enter a parse tree produced by reggieParser#coloringfilter. + def enterColoringfilter(self, ctx: reggieParser.ColoringfilterContext): + pass + + # Exit a parse tree produced by reggieParser#coloringfilter. + def exitColoringfilter(self, ctx: reggieParser.ColoringfilterContext): + pass + + # Enter a parse tree produced by reggieParser#structuralvertexpattern. + def enterStructuralvertexpattern( + self, ctx: reggieParser.StructuralvertexpatternContext + ): + pass + + # Exit a parse tree produced by reggieParser#structuralvertexpattern. + def exitStructuralvertexpattern( + self, ctx: reggieParser.StructuralvertexpatternContext + ): + pass + + # Enter a parse tree produced by reggieParser#intmodifier. + def enterIntmodifier(self, ctx: reggieParser.IntmodifierContext): + pass + + # Exit a parse tree produced by reggieParser#intmodifier. + def exitIntmodifier(self, ctx: reggieParser.IntmodifierContext): + pass + + # Enter a parse tree produced by reggieParser#starmodifier. + def enterStarmodifier(self, ctx: reggieParser.StarmodifierContext): + pass + + # Exit a parse tree produced by reggieParser#starmodifier. + def exitStarmodifier(self, ctx: reggieParser.StarmodifierContext): + pass + + # Enter a parse tree produced by reggieParser#plusmodifier. + def enterPlusmodifier(self, ctx: reggieParser.PlusmodifierContext): + pass + + # Exit a parse tree produced by reggieParser#plusmodifier. + def exitPlusmodifier(self, ctx: reggieParser.PlusmodifierContext): + pass + + # Enter a parse tree produced by reggieParser#structuralid. + def enterStructuralid(self, ctx: reggieParser.StructuralidContext): + pass + + # Exit a parse tree produced by reggieParser#structuralid. + def exitStructuralid(self, ctx: reggieParser.StructuralidContext): + pass + + # Enter a parse tree produced by reggieParser#labelfilter. + def enterLabelfilter(self, ctx: reggieParser.LabelfilterContext): + pass + + # Exit a parse tree produced by reggieParser#labelfilter. + def exitLabelfilter(self, ctx: reggieParser.LabelfilterContext): + pass + + # Enter a parse tree produced by reggieParser#label. + def enterLabel(self, ctx: reggieParser.LabelContext): + pass + + # Exit a parse tree produced by reggieParser#label. + def exitLabel(self, ctx: reggieParser.LabelContext): + pass + + # Enter a parse tree produced by reggieParser#vertex2vertex. + def enterVertex2vertex(self, ctx: reggieParser.Vertex2vertexContext): + pass + + # Exit a parse tree produced by reggieParser#vertex2vertex. + def exitVertex2vertex(self, ctx: reggieParser.Vertex2vertexContext): + pass + + # Enter a parse tree produced by reggieParser#edge. + def enterEdge(self, ctx: reggieParser.EdgeContext): + pass + + # Exit a parse tree produced by reggieParser#edge. + def exitEdge(self, ctx: reggieParser.EdgeContext): + pass + + +del reggieParser \ No newline at end of file diff --git a/lfr/antlrgen/reggie/reggieParser.py b/lfr/antlrgen/reggie/reggieParser.py new file mode 100644 index 0000000..dcf589e --- /dev/null +++ b/lfr/antlrgen/reggie/reggieParser.py @@ -0,0 +1,1180 @@ +# Generated from /Users/krishna/CIDAR/reggie/reggie.g4 by ANTLR 4.9 +# encoding: utf-8 +from antlr4 import * +from io import StringIO +import sys +if sys.version_info[1] > 5: + from typing import TextIO +else: + from typing.io import TextIO + + +def serializedATN(): + with StringIO() as buf: + buf.write("\3\u608b\ua72a\u8133\ub9ed\u417c\u3be7\u7786\u5964\3\25") + buf.write("\u0098\4\2\t\2\4\3\t\3\4\4\t\4\4\5\t\5\4\6\t\6\4\7\t\7") + buf.write("\4\b\t\b\4\t\t\t\4\n\t\n\4\13\t\13\4\f\t\f\4\r\t\r\4\16") + buf.write("\t\16\4\17\t\17\4\20\t\20\4\21\t\21\3\2\3\2\3\2\3\2\7") + buf.write("\2\'\n\2\f\2\16\2*\13\2\3\2\3\2\3\3\3\3\3\3\3\3\3\3\3") + buf.write("\3\5\3\64\n\3\3\4\3\4\3\4\5\49\n\4\3\5\3\5\3\5\5\5>\n") + buf.write("\5\3\6\3\6\3\6\3\6\3\6\3\6\3\6\7\6G\n\6\f\6\16\6J\13\6") + buf.write("\3\6\3\6\3\7\3\7\3\7\5\7Q\n\7\3\7\5\7T\n\7\3\7\7\7W\n") + buf.write("\7\f\7\16\7Z\13\7\3\b\3\b\3\b\3\b\7\b`\n\b\f\b\16\bc\13") + buf.write("\b\3\b\3\b\3\t\3\t\3\t\5\tj\n\t\3\n\3\n\3\n\3\n\3\13\3") + buf.write("\13\3\f\3\f\3\r\3\r\3\16\3\16\3\16\7\16y\n\16\f\16\16") + buf.write("\16|\13\16\3\16\3\16\3\16\3\16\7\16\u0082\n\16\f\16\16") + buf.write("\16\u0085\13\16\3\16\3\16\5\16\u0089\n\16\3\17\3\17\3") + buf.write("\20\3\20\3\20\3\20\7\20\u0091\n\20\f\20\16\20\u0094\13") + buf.write("\20\3\21\3\21\3\21\2\2\22\2\4\6\b\n\f\16\20\22\24\26\30") + buf.write("\32\34\36 \2\4\3\2\20\21\3\2\16\17\2\u0098\2\"\3\2\2\2") + buf.write("\4\63\3\2\2\2\68\3\2\2\2\b=\3\2\2\2\n?\3\2\2\2\fM\3\2") + buf.write("\2\2\16[\3\2\2\2\20i\3\2\2\2\22k\3\2\2\2\24o\3\2\2\2\26") + buf.write("q\3\2\2\2\30s\3\2\2\2\32\u0088\3\2\2\2\34\u008a\3\2\2") + buf.write("\2\36\u008c\3\2\2\2 \u0095\3\2\2\2\"#\7\3\2\2#(\5\4\3") + buf.write("\2$%\7\4\2\2%\'\5\4\3\2&$\3\2\2\2\'*\3\2\2\2(&\3\2\2\2") + buf.write("()\3\2\2\2)+\3\2\2\2*(\3\2\2\2+,\7\5\2\2,\3\3\2\2\2-\64") + buf.write("\5\b\5\2./\7\6\2\2/\60\5\b\5\2\60\61\7\7\2\2\61\62\5\6") + buf.write("\4\2\62\64\3\2\2\2\63-\3\2\2\2\63.\3\2\2\2\64\5\3\2\2") + buf.write("\2\659\5\22\n\2\669\5\26\f\2\679\5\24\13\28\65\3\2\2\2") + buf.write("8\66\3\2\2\28\67\3\2\2\29\7\3\2\2\2:>\5\n\6\2;>\5\36\20") + buf.write("\2<>\5\f\7\2=:\3\2\2\2=;\3\2\2\2=<\3\2\2\2>\t\3\2\2\2") + buf.write("?@\7\20\2\2@A\7\b\2\2AB\3\2\2\2BC\7\3\2\2CH\5\36\20\2") + buf.write("DE\7\4\2\2EG\5\36\20\2FD\3\2\2\2GJ\3\2\2\2HF\3\2\2\2H") + buf.write("I\3\2\2\2IK\3\2\2\2JH\3\2\2\2KL\7\5\2\2L\13\3\2\2\2MP") + buf.write("\5\30\r\2NO\7\b\2\2OQ\5\32\16\2PN\3\2\2\2PQ\3\2\2\2QS") + buf.write("\3\2\2\2RT\5\16\b\2SR\3\2\2\2ST\3\2\2\2TX\3\2\2\2UW\5") + buf.write("\20\t\2VU\3\2\2\2WZ\3\2\2\2XV\3\2\2\2XY\3\2\2\2Y\r\3\2") + buf.write("\2\2ZX\3\2\2\2[\\\7\3\2\2\\a\7\25\2\2]^\7\4\2\2^`\7\25") + buf.write("\2\2_]\3\2\2\2`c\3\2\2\2a_\3\2\2\2ab\3\2\2\2bd\3\2\2\2") + buf.write("ca\3\2\2\2de\7\5\2\2e\17\3\2\2\2fj\5\22\n\2gj\5\24\13") + buf.write("\2hj\5\26\f\2if\3\2\2\2ig\3\2\2\2ih\3\2\2\2j\21\3\2\2") + buf.write("\2kl\7\t\2\2lm\7\24\2\2mn\7\n\2\2n\23\3\2\2\2op\7\13\2") + buf.write("\2p\25\3\2\2\2qr\7\f\2\2r\27\3\2\2\2st\t\2\2\2t\31\3\2") + buf.write("\2\2uz\5\34\17\2vw\7\r\2\2wy\5\34\17\2xv\3\2\2\2y|\3\2") + buf.write("\2\2zx\3\2\2\2z{\3\2\2\2{\u0089\3\2\2\2|z\3\2\2\2}~\7") + buf.write("\6\2\2~\u0083\5\34\17\2\177\u0080\7\r\2\2\u0080\u0082") + buf.write("\5\34\17\2\u0081\177\3\2\2\2\u0082\u0085\3\2\2\2\u0083") + buf.write("\u0081\3\2\2\2\u0083\u0084\3\2\2\2\u0084\u0086\3\2\2\2") + buf.write("\u0085\u0083\3\2\2\2\u0086\u0087\7\7\2\2\u0087\u0089\3") + buf.write("\2\2\2\u0088u\3\2\2\2\u0088}\3\2\2\2\u0089\33\3\2\2\2") + buf.write("\u008a\u008b\7\20\2\2\u008b\35\3\2\2\2\u008c\u0092\5\f") + buf.write("\7\2\u008d\u008e\5 \21\2\u008e\u008f\5\f\7\2\u008f\u0091") + buf.write("\3\2\2\2\u0090\u008d\3\2\2\2\u0091\u0094\3\2\2\2\u0092") + buf.write("\u0090\3\2\2\2\u0092\u0093\3\2\2\2\u0093\37\3\2\2\2\u0094") + buf.write("\u0092\3\2\2\2\u0095\u0096\t\3\2\2\u0096!\3\2\2\2\20(") + buf.write("\638=HPSXaiz\u0083\u0088\u0092") + return buf.getvalue() + + +class reggieParser ( Parser ): + + grammarFileName = "reggie.g4" + + atn = ATNDeserializer().deserialize(serializedATN()) + + decisionsToDFA = [ DFA(ds, i) for i, ds in enumerate(atn.decisionToState) ] + + sharedContextCache = PredictionContextCache() + + literalNames = [ "", "'{'", "','", "'}'", "'('", "')'", "':'", + "'['", "']'", "'*'", "'+'", "'|'", "'->'", "'<->'", + "", "'?'" ] + + symbolicNames = [ "", "", "", "", + "", "", "", "", + "", "", "", "", + "", "", "ID", "QUESTIONMARK_WILDCARD", + "WS", "NL", "INT", "STRING" ] + + RULE_graph = 0 + RULE_graphstatement = 1 + RULE_statementmodifier = 2 + RULE_basestatement = 3 + RULE_subgraph = 4 + RULE_vertex = 5 + RULE_coloringfilter = 6 + RULE_structuralvertexpattern = 7 + RULE_intmodifier = 8 + RULE_starmodifier = 9 + RULE_plusmodifier = 10 + RULE_structuralid = 11 + RULE_labelfilter = 12 + RULE_label = 13 + RULE_vertex2vertex = 14 + RULE_edge = 15 + + ruleNames = [ "graph", "graphstatement", "statementmodifier", "basestatement", + "subgraph", "vertex", "coloringfilter", "structuralvertexpattern", + "intmodifier", "starmodifier", "plusmodifier", "structuralid", + "labelfilter", "label", "vertex2vertex", "edge" ] + + EOF = Token.EOF + T__0=1 + T__1=2 + T__2=3 + T__3=4 + T__4=5 + T__5=6 + T__6=7 + T__7=8 + T__8=9 + T__9=10 + T__10=11 + T__11=12 + T__12=13 + ID=14 + QUESTIONMARK_WILDCARD=15 + WS=16 + NL=17 + INT=18 + STRING=19 + + def __init__(self, input:TokenStream, output:TextIO = sys.stdout): + super().__init__(input, output) + self.checkVersion("4.9") + self._interp = ParserATNSimulator(self, self.atn, self.decisionsToDFA, self.sharedContextCache) + self._predicates = None + + + + + class GraphContext(ParserRuleContext): + + def __init__(self, parser, parent:ParserRuleContext=None, invokingState:int=-1): + super().__init__(parent, invokingState) + self.parser = parser + + def graphstatement(self, i:int=None): + if i is None: + return self.getTypedRuleContexts(reggieParser.GraphstatementContext) + else: + return self.getTypedRuleContext(reggieParser.GraphstatementContext,i) + + + def getRuleIndex(self): + return reggieParser.RULE_graph + + def enterRule(self, listener:ParseTreeListener): + if hasattr( listener, "enterGraph" ): + listener.enterGraph(self) + + def exitRule(self, listener:ParseTreeListener): + if hasattr( listener, "exitGraph" ): + listener.exitGraph(self) + + def accept(self, visitor:ParseTreeVisitor): + if hasattr( visitor, "visitGraph" ): + return visitor.visitGraph(self) + else: + return visitor.visitChildren(self) + + + + + def graph(self): + + localctx = reggieParser.GraphContext(self, self._ctx, self.state) + self.enterRule(localctx, 0, self.RULE_graph) + self._la = 0 # Token type + try: + self.enterOuterAlt(localctx, 1) + self.state = 32 + self.match(reggieParser.T__0) + self.state = 33 + self.graphstatement() + self.state = 38 + self._errHandler.sync(self) + _la = self._input.LA(1) + while _la==reggieParser.T__1: + self.state = 34 + self.match(reggieParser.T__1) + self.state = 35 + self.graphstatement() + self.state = 40 + self._errHandler.sync(self) + _la = self._input.LA(1) + + self.state = 41 + self.match(reggieParser.T__2) + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + + class GraphstatementContext(ParserRuleContext): + + def __init__(self, parser, parent:ParserRuleContext=None, invokingState:int=-1): + super().__init__(parent, invokingState) + self.parser = parser + + def basestatement(self): + return self.getTypedRuleContext(reggieParser.BasestatementContext,0) + + + def statementmodifier(self): + return self.getTypedRuleContext(reggieParser.StatementmodifierContext,0) + + + def getRuleIndex(self): + return reggieParser.RULE_graphstatement + + def enterRule(self, listener:ParseTreeListener): + if hasattr( listener, "enterGraphstatement" ): + listener.enterGraphstatement(self) + + def exitRule(self, listener:ParseTreeListener): + if hasattr( listener, "exitGraphstatement" ): + listener.exitGraphstatement(self) + + def accept(self, visitor:ParseTreeVisitor): + if hasattr( visitor, "visitGraphstatement" ): + return visitor.visitGraphstatement(self) + else: + return visitor.visitChildren(self) + + + + + def graphstatement(self): + + localctx = reggieParser.GraphstatementContext(self, self._ctx, self.state) + self.enterRule(localctx, 2, self.RULE_graphstatement) + try: + self.state = 49 + self._errHandler.sync(self) + token = self._input.LA(1) + if token in [reggieParser.ID, reggieParser.QUESTIONMARK_WILDCARD]: + self.enterOuterAlt(localctx, 1) + self.state = 43 + self.basestatement() + pass + elif token in [reggieParser.T__3]: + self.enterOuterAlt(localctx, 2) + self.state = 44 + self.match(reggieParser.T__3) + self.state = 45 + self.basestatement() + self.state = 46 + self.match(reggieParser.T__4) + self.state = 47 + self.statementmodifier() + pass + else: + raise NoViableAltException(self) + + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + + class StatementmodifierContext(ParserRuleContext): + + def __init__(self, parser, parent:ParserRuleContext=None, invokingState:int=-1): + super().__init__(parent, invokingState) + self.parser = parser + + def intmodifier(self): + return self.getTypedRuleContext(reggieParser.IntmodifierContext,0) + + + def plusmodifier(self): + return self.getTypedRuleContext(reggieParser.PlusmodifierContext,0) + + + def starmodifier(self): + return self.getTypedRuleContext(reggieParser.StarmodifierContext,0) + + + def getRuleIndex(self): + return reggieParser.RULE_statementmodifier + + def enterRule(self, listener:ParseTreeListener): + if hasattr( listener, "enterStatementmodifier" ): + listener.enterStatementmodifier(self) + + def exitRule(self, listener:ParseTreeListener): + if hasattr( listener, "exitStatementmodifier" ): + listener.exitStatementmodifier(self) + + def accept(self, visitor:ParseTreeVisitor): + if hasattr( visitor, "visitStatementmodifier" ): + return visitor.visitStatementmodifier(self) + else: + return visitor.visitChildren(self) + + + + + def statementmodifier(self): + + localctx = reggieParser.StatementmodifierContext(self, self._ctx, self.state) + self.enterRule(localctx, 4, self.RULE_statementmodifier) + try: + self.state = 54 + self._errHandler.sync(self) + token = self._input.LA(1) + if token in [reggieParser.T__6]: + self.enterOuterAlt(localctx, 1) + self.state = 51 + self.intmodifier() + pass + elif token in [reggieParser.T__9]: + self.enterOuterAlt(localctx, 2) + self.state = 52 + self.plusmodifier() + pass + elif token in [reggieParser.T__8]: + self.enterOuterAlt(localctx, 3) + self.state = 53 + self.starmodifier() + pass + else: + raise NoViableAltException(self) + + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + + class BasestatementContext(ParserRuleContext): + + def __init__(self, parser, parent:ParserRuleContext=None, invokingState:int=-1): + super().__init__(parent, invokingState) + self.parser = parser + + def subgraph(self): + return self.getTypedRuleContext(reggieParser.SubgraphContext,0) + + + def vertex2vertex(self): + return self.getTypedRuleContext(reggieParser.Vertex2vertexContext,0) + + + def vertex(self): + return self.getTypedRuleContext(reggieParser.VertexContext,0) + + + def getRuleIndex(self): + return reggieParser.RULE_basestatement + + def enterRule(self, listener:ParseTreeListener): + if hasattr( listener, "enterBasestatement" ): + listener.enterBasestatement(self) + + def exitRule(self, listener:ParseTreeListener): + if hasattr( listener, "exitBasestatement" ): + listener.exitBasestatement(self) + + def accept(self, visitor:ParseTreeVisitor): + if hasattr( visitor, "visitBasestatement" ): + return visitor.visitBasestatement(self) + else: + return visitor.visitChildren(self) + + + + + def basestatement(self): + + localctx = reggieParser.BasestatementContext(self, self._ctx, self.state) + self.enterRule(localctx, 6, self.RULE_basestatement) + try: + self.state = 59 + self._errHandler.sync(self) + la_ = self._interp.adaptivePredict(self._input,3,self._ctx) + if la_ == 1: + self.enterOuterAlt(localctx, 1) + self.state = 56 + self.subgraph() + pass + + elif la_ == 2: + self.enterOuterAlt(localctx, 2) + self.state = 57 + self.vertex2vertex() + pass + + elif la_ == 3: + self.enterOuterAlt(localctx, 3) + self.state = 58 + self.vertex() + pass + + + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + + class SubgraphContext(ParserRuleContext): + + def __init__(self, parser, parent:ParserRuleContext=None, invokingState:int=-1): + super().__init__(parent, invokingState) + self.parser = parser + + def vertex2vertex(self, i:int=None): + if i is None: + return self.getTypedRuleContexts(reggieParser.Vertex2vertexContext) + else: + return self.getTypedRuleContext(reggieParser.Vertex2vertexContext,i) + + + def ID(self): + return self.getToken(reggieParser.ID, 0) + + def getRuleIndex(self): + return reggieParser.RULE_subgraph + + def enterRule(self, listener:ParseTreeListener): + if hasattr( listener, "enterSubgraph" ): + listener.enterSubgraph(self) + + def exitRule(self, listener:ParseTreeListener): + if hasattr( listener, "exitSubgraph" ): + listener.exitSubgraph(self) + + def accept(self, visitor:ParseTreeVisitor): + if hasattr( visitor, "visitSubgraph" ): + return visitor.visitSubgraph(self) + else: + return visitor.visitChildren(self) + + + + + def subgraph(self): + + localctx = reggieParser.SubgraphContext(self, self._ctx, self.state) + self.enterRule(localctx, 8, self.RULE_subgraph) + self._la = 0 # Token type + try: + self.enterOuterAlt(localctx, 1) + self.state = 61 + self.match(reggieParser.ID) + self.state = 62 + self.match(reggieParser.T__5) + self.state = 64 + self.match(reggieParser.T__0) + self.state = 65 + self.vertex2vertex() + self.state = 70 + self._errHandler.sync(self) + _la = self._input.LA(1) + while _la==reggieParser.T__1: + self.state = 66 + self.match(reggieParser.T__1) + self.state = 67 + self.vertex2vertex() + self.state = 72 + self._errHandler.sync(self) + _la = self._input.LA(1) + + self.state = 73 + self.match(reggieParser.T__2) + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + + class VertexContext(ParserRuleContext): + + def __init__(self, parser, parent:ParserRuleContext=None, invokingState:int=-1): + super().__init__(parent, invokingState) + self.parser = parser + + def structuralid(self): + return self.getTypedRuleContext(reggieParser.StructuralidContext,0) + + + def labelfilter(self): + return self.getTypedRuleContext(reggieParser.LabelfilterContext,0) + + + def coloringfilter(self): + return self.getTypedRuleContext(reggieParser.ColoringfilterContext,0) + + + def structuralvertexpattern(self, i:int=None): + if i is None: + return self.getTypedRuleContexts(reggieParser.StructuralvertexpatternContext) + else: + return self.getTypedRuleContext(reggieParser.StructuralvertexpatternContext,i) + + + def getRuleIndex(self): + return reggieParser.RULE_vertex + + def enterRule(self, listener:ParseTreeListener): + if hasattr( listener, "enterVertex" ): + listener.enterVertex(self) + + def exitRule(self, listener:ParseTreeListener): + if hasattr( listener, "exitVertex" ): + listener.exitVertex(self) + + def accept(self, visitor:ParseTreeVisitor): + if hasattr( visitor, "visitVertex" ): + return visitor.visitVertex(self) + else: + return visitor.visitChildren(self) + + + + + def vertex(self): + + localctx = reggieParser.VertexContext(self, self._ctx, self.state) + self.enterRule(localctx, 10, self.RULE_vertex) + self._la = 0 # Token type + try: + self.enterOuterAlt(localctx, 1) + self.state = 75 + self.structuralid() + self.state = 78 + self._errHandler.sync(self) + _la = self._input.LA(1) + if _la==reggieParser.T__5: + self.state = 76 + self.match(reggieParser.T__5) + self.state = 77 + self.labelfilter() + + + self.state = 81 + self._errHandler.sync(self) + _la = self._input.LA(1) + if _la==reggieParser.T__0: + self.state = 80 + self.coloringfilter() + + + self.state = 86 + self._errHandler.sync(self) + _la = self._input.LA(1) + while (((_la) & ~0x3f) == 0 and ((1 << _la) & ((1 << reggieParser.T__6) | (1 << reggieParser.T__8) | (1 << reggieParser.T__9))) != 0): + self.state = 83 + self.structuralvertexpattern() + self.state = 88 + self._errHandler.sync(self) + _la = self._input.LA(1) + + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + + class ColoringfilterContext(ParserRuleContext): + + def __init__(self, parser, parent:ParserRuleContext=None, invokingState:int=-1): + super().__init__(parent, invokingState) + self.parser = parser + + def STRING(self, i:int=None): + if i is None: + return self.getTokens(reggieParser.STRING) + else: + return self.getToken(reggieParser.STRING, i) + + def getRuleIndex(self): + return reggieParser.RULE_coloringfilter + + def enterRule(self, listener:ParseTreeListener): + if hasattr( listener, "enterColoringfilter" ): + listener.enterColoringfilter(self) + + def exitRule(self, listener:ParseTreeListener): + if hasattr( listener, "exitColoringfilter" ): + listener.exitColoringfilter(self) + + def accept(self, visitor:ParseTreeVisitor): + if hasattr( visitor, "visitColoringfilter" ): + return visitor.visitColoringfilter(self) + else: + return visitor.visitChildren(self) + + + + + def coloringfilter(self): + + localctx = reggieParser.ColoringfilterContext(self, self._ctx, self.state) + self.enterRule(localctx, 12, self.RULE_coloringfilter) + self._la = 0 # Token type + try: + self.enterOuterAlt(localctx, 1) + self.state = 89 + self.match(reggieParser.T__0) + self.state = 90 + self.match(reggieParser.STRING) + self.state = 95 + self._errHandler.sync(self) + _la = self._input.LA(1) + while _la==reggieParser.T__1: + self.state = 91 + self.match(reggieParser.T__1) + self.state = 92 + self.match(reggieParser.STRING) + self.state = 97 + self._errHandler.sync(self) + _la = self._input.LA(1) + + self.state = 98 + self.match(reggieParser.T__2) + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + + class StructuralvertexpatternContext(ParserRuleContext): + + def __init__(self, parser, parent:ParserRuleContext=None, invokingState:int=-1): + super().__init__(parent, invokingState) + self.parser = parser + + def intmodifier(self): + return self.getTypedRuleContext(reggieParser.IntmodifierContext,0) + + + def starmodifier(self): + return self.getTypedRuleContext(reggieParser.StarmodifierContext,0) + + + def plusmodifier(self): + return self.getTypedRuleContext(reggieParser.PlusmodifierContext,0) + + + def getRuleIndex(self): + return reggieParser.RULE_structuralvertexpattern + + def enterRule(self, listener:ParseTreeListener): + if hasattr( listener, "enterStructuralvertexpattern" ): + listener.enterStructuralvertexpattern(self) + + def exitRule(self, listener:ParseTreeListener): + if hasattr( listener, "exitStructuralvertexpattern" ): + listener.exitStructuralvertexpattern(self) + + def accept(self, visitor:ParseTreeVisitor): + if hasattr( visitor, "visitStructuralvertexpattern" ): + return visitor.visitStructuralvertexpattern(self) + else: + return visitor.visitChildren(self) + + + + + def structuralvertexpattern(self): + + localctx = reggieParser.StructuralvertexpatternContext(self, self._ctx, self.state) + self.enterRule(localctx, 14, self.RULE_structuralvertexpattern) + try: + self.enterOuterAlt(localctx, 1) + self.state = 103 + self._errHandler.sync(self) + token = self._input.LA(1) + if token in [reggieParser.T__6]: + self.state = 100 + self.intmodifier() + pass + elif token in [reggieParser.T__8]: + self.state = 101 + self.starmodifier() + pass + elif token in [reggieParser.T__9]: + self.state = 102 + self.plusmodifier() + pass + else: + raise NoViableAltException(self) + + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + + class IntmodifierContext(ParserRuleContext): + + def __init__(self, parser, parent:ParserRuleContext=None, invokingState:int=-1): + super().__init__(parent, invokingState) + self.parser = parser + + def INT(self): + return self.getToken(reggieParser.INT, 0) + + def getRuleIndex(self): + return reggieParser.RULE_intmodifier + + def enterRule(self, listener:ParseTreeListener): + if hasattr( listener, "enterIntmodifier" ): + listener.enterIntmodifier(self) + + def exitRule(self, listener:ParseTreeListener): + if hasattr( listener, "exitIntmodifier" ): + listener.exitIntmodifier(self) + + def accept(self, visitor:ParseTreeVisitor): + if hasattr( visitor, "visitIntmodifier" ): + return visitor.visitIntmodifier(self) + else: + return visitor.visitChildren(self) + + + + + def intmodifier(self): + + localctx = reggieParser.IntmodifierContext(self, self._ctx, self.state) + self.enterRule(localctx, 16, self.RULE_intmodifier) + try: + self.enterOuterAlt(localctx, 1) + self.state = 105 + self.match(reggieParser.T__6) + self.state = 106 + self.match(reggieParser.INT) + self.state = 107 + self.match(reggieParser.T__7) + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + + class StarmodifierContext(ParserRuleContext): + + def __init__(self, parser, parent:ParserRuleContext=None, invokingState:int=-1): + super().__init__(parent, invokingState) + self.parser = parser + + + def getRuleIndex(self): + return reggieParser.RULE_starmodifier + + def enterRule(self, listener:ParseTreeListener): + if hasattr( listener, "enterStarmodifier" ): + listener.enterStarmodifier(self) + + def exitRule(self, listener:ParseTreeListener): + if hasattr( listener, "exitStarmodifier" ): + listener.exitStarmodifier(self) + + def accept(self, visitor:ParseTreeVisitor): + if hasattr( visitor, "visitStarmodifier" ): + return visitor.visitStarmodifier(self) + else: + return visitor.visitChildren(self) + + + + + def starmodifier(self): + + localctx = reggieParser.StarmodifierContext(self, self._ctx, self.state) + self.enterRule(localctx, 18, self.RULE_starmodifier) + try: + self.enterOuterAlt(localctx, 1) + self.state = 109 + self.match(reggieParser.T__8) + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + + class PlusmodifierContext(ParserRuleContext): + + def __init__(self, parser, parent:ParserRuleContext=None, invokingState:int=-1): + super().__init__(parent, invokingState) + self.parser = parser + + + def getRuleIndex(self): + return reggieParser.RULE_plusmodifier + + def enterRule(self, listener:ParseTreeListener): + if hasattr( listener, "enterPlusmodifier" ): + listener.enterPlusmodifier(self) + + def exitRule(self, listener:ParseTreeListener): + if hasattr( listener, "exitPlusmodifier" ): + listener.exitPlusmodifier(self) + + def accept(self, visitor:ParseTreeVisitor): + if hasattr( visitor, "visitPlusmodifier" ): + return visitor.visitPlusmodifier(self) + else: + return visitor.visitChildren(self) + + + + + def plusmodifier(self): + + localctx = reggieParser.PlusmodifierContext(self, self._ctx, self.state) + self.enterRule(localctx, 20, self.RULE_plusmodifier) + try: + self.enterOuterAlt(localctx, 1) + self.state = 111 + self.match(reggieParser.T__9) + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + + class StructuralidContext(ParserRuleContext): + + def __init__(self, parser, parent:ParserRuleContext=None, invokingState:int=-1): + super().__init__(parent, invokingState) + self.parser = parser + + def ID(self): + return self.getToken(reggieParser.ID, 0) + + def QUESTIONMARK_WILDCARD(self): + return self.getToken(reggieParser.QUESTIONMARK_WILDCARD, 0) + + def getRuleIndex(self): + return reggieParser.RULE_structuralid + + def enterRule(self, listener:ParseTreeListener): + if hasattr( listener, "enterStructuralid" ): + listener.enterStructuralid(self) + + def exitRule(self, listener:ParseTreeListener): + if hasattr( listener, "exitStructuralid" ): + listener.exitStructuralid(self) + + def accept(self, visitor:ParseTreeVisitor): + if hasattr( visitor, "visitStructuralid" ): + return visitor.visitStructuralid(self) + else: + return visitor.visitChildren(self) + + + + + def structuralid(self): + + localctx = reggieParser.StructuralidContext(self, self._ctx, self.state) + self.enterRule(localctx, 22, self.RULE_structuralid) + self._la = 0 # Token type + try: + self.enterOuterAlt(localctx, 1) + self.state = 113 + _la = self._input.LA(1) + if not(_la==reggieParser.ID or _la==reggieParser.QUESTIONMARK_WILDCARD): + self._errHandler.recoverInline(self) + else: + self._errHandler.reportMatch(self) + self.consume() + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + + class LabelfilterContext(ParserRuleContext): + + def __init__(self, parser, parent:ParserRuleContext=None, invokingState:int=-1): + super().__init__(parent, invokingState) + self.parser = parser + + def label(self, i:int=None): + if i is None: + return self.getTypedRuleContexts(reggieParser.LabelContext) + else: + return self.getTypedRuleContext(reggieParser.LabelContext,i) + + + def getRuleIndex(self): + return reggieParser.RULE_labelfilter + + def enterRule(self, listener:ParseTreeListener): + if hasattr( listener, "enterLabelfilter" ): + listener.enterLabelfilter(self) + + def exitRule(self, listener:ParseTreeListener): + if hasattr( listener, "exitLabelfilter" ): + listener.exitLabelfilter(self) + + def accept(self, visitor:ParseTreeVisitor): + if hasattr( visitor, "visitLabelfilter" ): + return visitor.visitLabelfilter(self) + else: + return visitor.visitChildren(self) + + + + + def labelfilter(self): + + localctx = reggieParser.LabelfilterContext(self, self._ctx, self.state) + self.enterRule(localctx, 24, self.RULE_labelfilter) + self._la = 0 # Token type + try: + self.state = 134 + self._errHandler.sync(self) + token = self._input.LA(1) + if token in [reggieParser.ID]: + self.enterOuterAlt(localctx, 1) + self.state = 115 + self.label() + self.state = 120 + self._errHandler.sync(self) + _la = self._input.LA(1) + while _la==reggieParser.T__10: + self.state = 116 + self.match(reggieParser.T__10) + self.state = 117 + self.label() + self.state = 122 + self._errHandler.sync(self) + _la = self._input.LA(1) + + pass + elif token in [reggieParser.T__3]: + self.enterOuterAlt(localctx, 2) + self.state = 123 + self.match(reggieParser.T__3) + self.state = 124 + self.label() + self.state = 129 + self._errHandler.sync(self) + _la = self._input.LA(1) + while _la==reggieParser.T__10: + self.state = 125 + self.match(reggieParser.T__10) + self.state = 126 + self.label() + self.state = 131 + self._errHandler.sync(self) + _la = self._input.LA(1) + + self.state = 132 + self.match(reggieParser.T__4) + pass + else: + raise NoViableAltException(self) + + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + + class LabelContext(ParserRuleContext): + + def __init__(self, parser, parent:ParserRuleContext=None, invokingState:int=-1): + super().__init__(parent, invokingState) + self.parser = parser + + def ID(self): + return self.getToken(reggieParser.ID, 0) + + def getRuleIndex(self): + return reggieParser.RULE_label + + def enterRule(self, listener:ParseTreeListener): + if hasattr( listener, "enterLabel" ): + listener.enterLabel(self) + + def exitRule(self, listener:ParseTreeListener): + if hasattr( listener, "exitLabel" ): + listener.exitLabel(self) + + def accept(self, visitor:ParseTreeVisitor): + if hasattr( visitor, "visitLabel" ): + return visitor.visitLabel(self) + else: + return visitor.visitChildren(self) + + + + + def label(self): + + localctx = reggieParser.LabelContext(self, self._ctx, self.state) + self.enterRule(localctx, 26, self.RULE_label) + try: + self.enterOuterAlt(localctx, 1) + self.state = 136 + self.match(reggieParser.ID) + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + + class Vertex2vertexContext(ParserRuleContext): + + def __init__(self, parser, parent:ParserRuleContext=None, invokingState:int=-1): + super().__init__(parent, invokingState) + self.parser = parser + + def vertex(self, i:int=None): + if i is None: + return self.getTypedRuleContexts(reggieParser.VertexContext) + else: + return self.getTypedRuleContext(reggieParser.VertexContext,i) + + + def edge(self, i:int=None): + if i is None: + return self.getTypedRuleContexts(reggieParser.EdgeContext) + else: + return self.getTypedRuleContext(reggieParser.EdgeContext,i) + + + def getRuleIndex(self): + return reggieParser.RULE_vertex2vertex + + def enterRule(self, listener:ParseTreeListener): + if hasattr( listener, "enterVertex2vertex" ): + listener.enterVertex2vertex(self) + + def exitRule(self, listener:ParseTreeListener): + if hasattr( listener, "exitVertex2vertex" ): + listener.exitVertex2vertex(self) + + def accept(self, visitor:ParseTreeVisitor): + if hasattr( visitor, "visitVertex2vertex" ): + return visitor.visitVertex2vertex(self) + else: + return visitor.visitChildren(self) + + + + + def vertex2vertex(self): + + localctx = reggieParser.Vertex2vertexContext(self, self._ctx, self.state) + self.enterRule(localctx, 28, self.RULE_vertex2vertex) + self._la = 0 # Token type + try: + self.enterOuterAlt(localctx, 1) + self.state = 138 + self.vertex() + self.state = 144 + self._errHandler.sync(self) + _la = self._input.LA(1) + while _la==reggieParser.T__11 or _la==reggieParser.T__12: + self.state = 139 + self.edge() + self.state = 140 + self.vertex() + self.state = 146 + self._errHandler.sync(self) + _la = self._input.LA(1) + + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + + class EdgeContext(ParserRuleContext): + + def __init__(self, parser, parent:ParserRuleContext=None, invokingState:int=-1): + super().__init__(parent, invokingState) + self.parser = parser + + + def getRuleIndex(self): + return reggieParser.RULE_edge + + def enterRule(self, listener:ParseTreeListener): + if hasattr( listener, "enterEdge" ): + listener.enterEdge(self) + + def exitRule(self, listener:ParseTreeListener): + if hasattr( listener, "exitEdge" ): + listener.exitEdge(self) + + def accept(self, visitor:ParseTreeVisitor): + if hasattr( visitor, "visitEdge" ): + return visitor.visitEdge(self) + else: + return visitor.visitChildren(self) + + + + + def edge(self): + + localctx = reggieParser.EdgeContext(self, self._ctx, self.state) + self.enterRule(localctx, 30, self.RULE_edge) + self._la = 0 # Token type + try: + self.enterOuterAlt(localctx, 1) + self.state = 147 + _la = self._input.LA(1) + if not(_la==reggieParser.T__11 or _la==reggieParser.T__12): + self._errHandler.recoverInline(self) + else: + self._errHandler.reportMatch(self) + self.consume() + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + + + + diff --git a/lfr/antlrgen/reggie/reggieVisitor.py b/lfr/antlrgen/reggie/reggieVisitor.py new file mode 100644 index 0000000..e90b859 --- /dev/null +++ b/lfr/antlrgen/reggie/reggieVisitor.py @@ -0,0 +1,81 @@ +# Generated from /Users/krishna/CIDAR/reggie/reggie.g4 by ANTLR 4.9 +from antlr4 import * + +if __name__ is not None and "." in __name__: + from .reggieParser import reggieParser +else: + from lfr.graphmatchParser import reggieParser + +# This class defines a complete generic visitor for a parse tree produced by reggieParser. + + +class reggieVisitor(ParseTreeVisitor): + + # Visit a parse tree produced by reggieParser#graph. + def visitGraph(self, ctx: reggieParser.GraphContext): + return self.visitChildren(ctx) + + # Visit a parse tree produced by reggieParser#graphstatement. + def visitGraphstatement(self, ctx: reggieParser.GraphstatementContext): + return self.visitChildren(ctx) + + # Visit a parse tree produced by reggieParser#statementmodifier. + def visitStatementmodifier(self, ctx: reggieParser.StatementmodifierContext): + return self.visitChildren(ctx) + + # Visit a parse tree produced by reggieParser#basestatement. + def visitBasestatement(self, ctx: reggieParser.BasestatementContext): + return self.visitChildren(ctx) + + # Visit a parse tree produced by reggieParser#subgraph. + def visitSubgraph(self, ctx: reggieParser.SubgraphContext): + return self.visitChildren(ctx) + + # Visit a parse tree produced by reggieParser#vertex. + def visitVertex(self, ctx: reggieParser.VertexContext): + return self.visitChildren(ctx) + + # Visit a parse tree produced by reggieParser#coloringfilter. + def visitColoringfilter(self, ctx: reggieParser.ColoringfilterContext): + return self.visitChildren(ctx) + + # Visit a parse tree produced by reggieParser#structuralvertexpattern. + def visitStructuralvertexpattern( + self, ctx: reggieParser.StructuralvertexpatternContext + ): + return self.visitChildren(ctx) + + # Visit a parse tree produced by reggieParser#intmodifier. + def visitIntmodifier(self, ctx: reggieParser.IntmodifierContext): + return self.visitChildren(ctx) + + # Visit a parse tree produced by reggieParser#starmodifier. + def visitStarmodifier(self, ctx: reggieParser.StarmodifierContext): + return self.visitChildren(ctx) + + # Visit a parse tree produced by reggieParser#plusmodifier. + def visitPlusmodifier(self, ctx: reggieParser.PlusmodifierContext): + return self.visitChildren(ctx) + + # Visit a parse tree produced by reggieParser#structuralid. + def visitStructuralid(self, ctx: reggieParser.StructuralidContext): + return self.visitChildren(ctx) + + # Visit a parse tree produced by reggieParser#labelfilter. + def visitLabelfilter(self, ctx: reggieParser.LabelfilterContext): + return self.visitChildren(ctx) + + # Visit a parse tree produced by reggieParser#label. + def visitLabel(self, ctx: reggieParser.LabelContext): + return self.visitChildren(ctx) + + # Visit a parse tree produced by reggieParser#vertex2vertex. + def visitVertex2vertex(self, ctx: reggieParser.Vertex2vertexContext): + return self.visitChildren(ctx) + + # Visit a parse tree produced by reggieParser#edge. + def visitEdge(self, ctx: reggieParser.EdgeContext): + return self.visitChildren(ctx) + + +del reggieParser \ No newline at end of file diff --git a/lfr/graphmatch/README.md b/lfr/graphmatch/README.md new file mode 100644 index 0000000..0a9d138 --- /dev/null +++ b/lfr/graphmatch/README.md @@ -0,0 +1,886 @@ + + +## Queries + +Match queries for MINT Componnet library 2021 Spring + +### PORT + +``` +{ + v1:IO +} +``` + +### MIXER + +``` +{ + v1:MIX +} +``` + +### DROPLET CAPACITANCE SENSOR + +``` +{ + v1:PROCESS +} +``` + +### LONG CELL TRAP + +``` +{ + v1:STORAGE +} +``` + +### SQUARE CELL TRAP + +``` +{ + v1:STORAGE +} +``` + +### REACTION CHAMBER + +``` +{ + v1:STROAGE +} +``` + +### CHEMOSTAT RING + +``` +{ + v1:STORAGE +} +``` + +### CURVED MIXER + +``` +{ + v1:MIX +} +``` + +### DIAMOND REACTION CHAMBER + +``` +{ + v1:STORAGE +} +``` + +### NOZZLE DROPLET GENERATOR + +``` +{ + v1:METER +} +``` + +### DROPLET GENERATOR FLOW FOCUS + +``` +{ + v1:IO -> v2:METER +} +``` + +### DROPLET GENERATOR T + +``` +{ + v1:METER +} +``` + +### DROPLET MERGER + +``` +{ + v1:MIX +} +``` + +### DROPLET SPLITTER +``` +{ + v1:DIVIDE +} +``` + +### FILTER + +``` +{ + v1:PROCESS +} +``` + +### GRADIENT GENERATOR + +TODO - Figure out how to show this as the gradient generator +``` +{ + ??????? +} +``` + +### LL CHAMBER +TODO - Change the name of this + +``` +{ + v1:MIX -> v2:STORAGE +} +``` + +### LOGIC ARRAY + +TODO - Figure out how to control sequences work from an LFR file. Also figure out where the + +``` +{ + v1:STORAGE <-> v2:STORAGE, + v1:STORAGE <-> v3:STORAGE, + v1:STORAGE <-> v4:STORAGE, + v2:STORAGE <-> v3:STORAGE, + v3:STORAGE <-> v4:STORAGE +} +``` + +### DROPLET MERGER + +``` +{ + v1:MIX +} +``` + +### MUX + +### Output MUX + +### 1->2 + +``` +{ + v1 { "DISTRIBUTE_OR", "or_1" }, + v1 -> vo1 { "DISTRIBUTE_OR", "or_1" }, + v1 -> vo2 { "DISTRIBUTE_OR", "or_1" } +} +``` + +### 1->4 + +``` +{ + v1 { "DISTRIBUTE_OR", "or_1" }, + v1 -> vo1 { "DISTRIBUTE_OR", "or_1" }, + v1 -> vo2 { "DISTRIBUTE_OR", "or_1" }, + v1 -> vo3 { "DISTRIBUTE_OR", "or_1" }, + v1 -> vo4 { "DISTRIBUTE_OR", "or_1" } +} +``` + +### 1->8 + +``` +{ + v1 { "DISTRIBUTE_OR", "or_1" }, + v1 -> vo1 { "DISTRIBUTE_OR", "or_1" }, + v1 -> vo2 { "DISTRIBUTE_OR", "or_1" }, + v1 -> vo3 { "DISTRIBUTE_OR", "or_1" }, + v1 -> vo4 { "DISTRIBUTE_OR", "or_1" }, + v1 -> vo5 { "DISTRIBUTE_OR", "or_1" }, + v1 -> vo6 { "DISTRIBUTE_OR", "or_1" }, + v1 -> vo7 { "DISTRIBUTE_OR", "or_1" }, + v1 -> vo8 { "DISTRIBUTE_OR", "or_1" } +} +``` + + +### 1->16 + +``` +{ + v1 { "DISTRIBUTE_OR", "or_1" }, + v1 -> vo1 { "DISTRIBUTE_OR", "or_1" }, + v1 -> vo2 { "DISTRIBUTE_OR", "or_1" }, + v1 -> vo3 { "DISTRIBUTE_OR", "or_1" }, + v1 -> vo4 { "DISTRIBUTE_OR", "or_1" }, + v1 -> vo5 { "DISTRIBUTE_OR", "or_1" }, + v1 -> vo6 { "DISTRIBUTE_OR", "or_1" }, + v1 -> vo7 { "DISTRIBUTE_OR", "or_1" }, + v1 -> vo8 { "DISTRIBUTE_OR", "or_1" }, + v1 -> vo9 { "DISTRIBUTE_OR", "or_1" }, + v1 -> vo10 { "DISTRIBUTE_OR", "or_1" }, + v1 -> vo11 { "DISTRIBUTE_OR", "or_1" }, + v1 -> vo12 { "DISTRIBUTE_OR", "or_1" }, + v1 -> vo13 { "DISTRIBUTE_OR", "or_1" }, + v1 -> vo14 { "DISTRIBUTE_OR", "or_1" }, + v1 -> vo15 { "DISTRIBUTE_OR", "or_1" }, + v1 -> vo16 { "DISTRIBUTE_OR", "or_1" } +} +``` + + +### 1->32 + +``` +{ + v1 { "DISTRIBUTE_OR", "or_1" }, + v1 -> vo1 { "DISTRIBUTE_OR", "or_1" }, + v1 -> vo2 { "DISTRIBUTE_OR", "or_1" }, + v1 -> vo3 { "DISTRIBUTE_OR", "or_1" }, + v1 -> vo4 { "DISTRIBUTE_OR", "or_1" }, + v1 -> vo5 { "DISTRIBUTE_OR", "or_1" }, + v1 -> vo6 { "DISTRIBUTE_OR", "or_1" }, + v1 -> vo7 { "DISTRIBUTE_OR", "or_1" }, + v1 -> vo8 { "DISTRIBUTE_OR", "or_1" }, + v1 -> vo9 { "DISTRIBUTE_OR", "or_1" }, + v1 -> vo10 { "DISTRIBUTE_OR", "or_1" }, + v1 -> vo11 { "DISTRIBUTE_OR", "or_1" }, + v1 -> vo12 { "DISTRIBUTE_OR", "or_1" }, + v1 -> vo13 { "DISTRIBUTE_OR", "or_1" }, + v1 -> vo14 { "DISTRIBUTE_OR", "or_1" }, + v1 -> vo15 { "DISTRIBUTE_OR", "or_1" }, + v1 -> vo16 { "DISTRIBUTE_OR", "or_1" }, + v1 -> vo17 { "DISTRIBUTE_OR", "or_1" }, + v1 -> vo18 { "DISTRIBUTE_OR", "or_1" }, + v1 -> vo19 { "DISTRIBUTE_OR", "or_1" }, + v1 -> vo20 { "DISTRIBUTE_OR", "or_1" }, + v1 -> vo21 { "DISTRIBUTE_OR", "or_1" }, + v1 -> vo22 { "DISTRIBUTE_OR", "or_1" }, + v1 -> vo23 { "DISTRIBUTE_OR", "or_1" }, + v1 -> vo24 { "DISTRIBUTE_OR", "or_1" }, + v1 -> vo25 { "DISTRIBUTE_OR", "or_1" }, + v1 -> vo26 { "DISTRIBUTE_OR", "or_1" }, + v1 -> vo27 { "DISTRIBUTE_OR", "or_1" }, + v1 -> vo28 { "DISTRIBUTE_OR", "or_1" }, + v1 -> vo29 { "DISTRIBUTE_OR", "or_1" }, + v1 -> vo30 { "DISTRIBUTE_OR", "or_1" }, + v1 -> vo31 { "DISTRIBUTE_OR", "or_1" }, + v1 -> vo32 { "DISTRIBUTE_OR", "or_1" } +} +``` + +### Input MUX + +### 2->1 +``` +{ + v1 { "DISTRIBUTE_OR", "or_1" }, + vi1 { "DISTRIBUTE_OR", "or_1" } -> v1, + vi2 { "DISTRIBUTE_OR", "or_1" } -> v1 +} +``` + +### 4->1 +``` +{ + v1 { "DISTRIBUTE_OR", "or_1" }, + vi1 { "DISTRIBUTE_OR", "or_1" } -> v1, + vi2 { "DISTRIBUTE_OR", "or_1" } -> v1, + vi3 { "DISTRIBUTE_OR", "or_1" } -> v1, + vi4 { "DISTRIBUTE_OR", "or_1" } -> v1 +} +``` + +### 8->1 +``` +{ + v1 { "DISTRIBUTE_OR", "or_1" }, + vi1 { "DISTRIBUTE_OR", "or_1" } -> v1, + vi2 { "DISTRIBUTE_OR", "or_1" } -> v1, + vi3 { "DISTRIBUTE_OR", "or_1" } -> v1, + vi4 { "DISTRIBUTE_OR", "or_1" } -> v1, + vi5 { "DISTRIBUTE_OR", "or_1" } -> v1, + vi6 { "DISTRIBUTE_OR", "or_1" } -> v1, + vi7 { "DISTRIBUTE_OR", "or_1" } -> v1, + vi8 { "DISTRIBUTE_OR", "or_1" } -> v1 +} +``` + +### 16->1 +``` +{ + v1 { "DISTRIBUTE_OR", "or_1" }, + vi1 { "DISTRIBUTE_OR", "or_1" } -> v1, + vi2 { "DISTRIBUTE_OR", "or_1" } -> v1, + vi3 { "DISTRIBUTE_OR", "or_1" } -> v1, + vi4 { "DISTRIBUTE_OR", "or_1" } -> v1, + vi5 { "DISTRIBUTE_OR", "or_1" } -> v1, + vi6 { "DISTRIBUTE_OR", "or_1" } -> v1, + vi7 { "DISTRIBUTE_OR", "or_1" } -> v1, + vi8 { "DISTRIBUTE_OR", "or_1" } -> v1, + vi9 { "DISTRIBUTE_OR", "or_1" } -> v1, + vi10 { "DISTRIBUTE_OR", "or_1" } -> v1, + vi11 { "DISTRIBUTE_OR", "or_1" } -> v1, + vi12 { "DISTRIBUTE_OR", "or_1" } -> v1, + vi13 { "DISTRIBUTE_OR", "or_1" } -> v1, + vi14 { "DISTRIBUTE_OR", "or_1" } -> v1, + vi15 { "DISTRIBUTE_OR", "or_1" } -> v1, + vi16 { "DISTRIBUTE_OR", "or_1" } -> v1 +} +``` + +### 32->1 +``` +{ + v1 { "DISTRIBUTE_OR", "or_1" }, + vi1 { "DISTRIBUTE_OR", "or_1" } -> v1, + vi2 { "DISTRIBUTE_OR", "or_1" } -> v1, + vi3 { "DISTRIBUTE_OR", "or_1" } -> v1, + vi4 { "DISTRIBUTE_OR", "or_1" } -> v1, + vi5 { "DISTRIBUTE_OR", "or_1" } -> v1, + vi6 { "DISTRIBUTE_OR", "or_1" } -> v1, + vi7 { "DISTRIBUTE_OR", "or_1" } -> v1, + vi8 { "DISTRIBUTE_OR", "or_1" } -> v1, + vi9 { "DISTRIBUTE_OR", "or_1" } -> v1, + vi10 { "DISTRIBUTE_OR", "or_1" } -> v1, + vi11 { "DISTRIBUTE_OR", "or_1" } -> v1, + vi12 { "DISTRIBUTE_OR", "or_1" } -> v1, + vi13 { "DISTRIBUTE_OR", "or_1" } -> v1, + vi14 { "DISTRIBUTE_OR", "or_1" } -> v1, + vi15 { "DISTRIBUTE_OR", "or_1" } -> v1, + vi16 { "DISTRIBUTE_OR", "or_1" } -> v1, + vi17 { "DISTRIBUTE_OR", "or_1" } -> v1, + vi18 { "DISTRIBUTE_OR", "or_1" } -> v1, + vi19 { "DISTRIBUTE_OR", "or_1" } -> v1, + vi20 { "DISTRIBUTE_OR", "or_1" } -> v1, + vi21 { "DISTRIBUTE_OR", "or_1" } -> v1, + vi22 { "DISTRIBUTE_OR", "or_1" } -> v1, + vi23 { "DISTRIBUTE_OR", "or_1" } -> v1, + vi24 { "DISTRIBUTE_OR", "or_1" } -> v1, + vi25 { "DISTRIBUTE_OR", "or_1" } -> v1, + vi26 { "DISTRIBUTE_OR", "or_1" } -> v1, + vi27 { "DISTRIBUTE_OR", "or_1" } -> v1, + vi28 { "DISTRIBUTE_OR", "or_1" } -> v1, + vi29 { "DISTRIBUTE_OR", "or_1" } -> v1, + vi30 { "DISTRIBUTE_OR", "or_1" } -> v1, + vi31 { "DISTRIBUTE_OR", "or_1" } -> v1, + vi32 { "DISTRIBUTE_OR", "or_1" } -> v1 +} +``` + + +### PATTERN BASED + +``` +{ + ? { "or_1" } -> v1, + (? { "or_1" } -> v1)+ +} +``` + +``` +{ + v1 -> ? { "or_1" }, + (v1 -> ? { "or_1" })+ +} +``` + +### PICOINJECTOR +``` +{ + v1:MIX +} +``` + +### PORT + +``` +{ + v1:IO +} +``` + +### PUMP + +``` +{ + v1:PUMP +} +``` + +### PUMP3D + +``` +{ + v1:PUMP +} +``` + +### ROTARY MIXER + +``` +{ + v1:MIX -> v2:STROAGE +} +``` + +``` +{ + v1:STORAGE -> v2:MIX +} +``` + +### DROPLET SORTER + +``` +{ + v1:SIEVE +} +``` + +### MIXER3D + +``` +{ + v1:MIX +} +``` + +### TRANSPOSER + +``` +{ + +} +``` + +### TREE + +### Output + +### 1->2 + +``` +{ + v1 -> vo1, + v1 -> vo2 +} +``` + +### 1->4 + +``` +{ + v1 -> vo1, + v1 -> vo2, + v1 -> vo3, + v1 -> vo4 +} +``` + +### 1->8 + +``` +{ + v1 -> vo1, + v1 -> vo2, + v1 -> vo3, + v1 -> vo4, + v1 -> vo5, + v1 -> vo6, + v1 -> vo7, + v1 -> vo8 +} +``` + + +### 1->16 + +``` +{ + v1 -> vo1, + v1 -> vo2, + v1 -> vo3, + v1 -> vo4, + v1 -> vo5, + v1 -> vo6, + v1 -> vo7, + v1 -> vo8, + v1 -> vo9, + v1 -> vo10, + v1 -> vo11, + v1 -> vo12, + v1 -> vo13, + v1 -> vo14, + v1 -> vo15, + v1 -> vo16 +} +``` + + +### 1->32 + +``` +{ + v1 -> vo1 , + v1 -> vo2 , + v1 -> vo3 , + v1 -> vo4 , + v1 -> vo5 , + v1 -> vo6 , + v1 -> vo7 , + v1 -> vo8 , + v1 -> vo9 , + v1 -> vo10 , + v1 -> vo11 , + v1 -> vo12 , + v1 -> vo13 , + v1 -> vo14 , + v1 -> vo15 , + v1 -> vo16 , + v1 -> vo17 , + v1 -> vo18 , + v1 -> vo19 , + v1 -> vo20 , + v1 -> vo21 , + v1 -> vo22 , + v1 -> vo23 , + v1 -> vo24 , + v1 -> vo25 , + v1 -> vo26 , + v1 -> vo27 , + v1 -> vo28 , + v1 -> vo29 , + v1 -> vo30 , + v1 -> vo31 , + v1 -> vo32 +} +``` + +### Input + +### 2->1 +``` +{ + vi1 -> v1, + vi2 -> v1 +} +``` + +### 4->1 +``` +{ + vi1 -> v1, + vi2 -> v1, + vi3 -> v1, + vi4 -> v1 +} +``` + +### 8->1 +``` +{ + vi1 -> v1, + vi2 -> v1, + vi3 -> v1, + vi4 -> v1, + vi5 -> v1, + vi6 -> v1, + vi7 -> v1, + vi8 -> v1 +} +``` + +### 16->1 +``` +{ + vi1 -> v1, + vi2 -> v1, + vi3 -> v1, + vi4 -> v1, + vi5 -> v1, + vi6 -> v1, + vi7 -> v1, + vi8 -> v1, + vi9 -> v1, + vi10 -> v1, + vi11 -> v1, + vi12 -> v1, + vi13 -> v1, + vi14 -> v1, + vi15 -> v1, + vi16 -> v1 +} +``` + +### 32->1 +``` +{ + vi1 -> v1, + vi2 -> v1, + vi3 -> v1, + vi4 -> v1, + vi5 -> v1, + vi6 -> v1, + vi7 -> v1, + vi8 -> v1, + vi9 -> v1, + vi10 -> v1, + vi11 -> v1, + vi12 -> v1, + vi13 -> v1, + vi14 -> v1, + vi15 -> v1, + vi16 -> v1, + vi17 -> v1, + vi18 -> v1, + vi19 -> v1, + vi20 -> v1, + vi21 -> v1, + vi22 -> v1, + vi23 -> v1, + vi24 -> v1, + vi25 -> v1, + vi26 -> v1, + vi27 -> v1, + vi28 -> v1, + vi29 -> v1, + vi30 -> v1, + vi31 -> v1, + vi32 -> v1 +} +``` + + + + +### Pattern Based +``` +{ + v1:FLOW -> ?:FLOW, + (v1:FLOW -> ?:FLOW)+ +} +``` + +``` +{ + ?:FLOW -> v1:FLOW, + (?: FLOW -> v1:FLOW)+ +} +``` + +### YTREE + +### Output + +### 1->2 + +``` +{ + v1 -> vo1, + v1 -> vo2 +} +``` + +### 1->4 + +``` +{ + v1 -> vo1, + v1 -> vo2, + v1 -> vo3, + v1 -> vo4 +} +``` + +### 1->8 + +``` +{ + v1 -> vo1, + v1 -> vo2, + v1 -> vo3, + v1 -> vo4, + v1 -> vo5, + v1 -> vo6, + v1 -> vo7, + v1 -> vo8 +} +``` + + +### 1->16 + +``` +{ + v1 -> vo1, + v1 -> vo2, + v1 -> vo3, + v1 -> vo4, + v1 -> vo5, + v1 -> vo6, + v1 -> vo7, + v1 -> vo8, + v1 -> vo9, + v1 -> vo10, + v1 -> vo11, + v1 -> vo12, + v1 -> vo13, + v1 -> vo14, + v1 -> vo15, + v1 -> vo16 +} +``` + + +### 1->32 + +``` +{ + v1 -> vo1 , + v1 -> vo2 , + v1 -> vo3 , + v1 -> vo4 , + v1 -> vo5 , + v1 -> vo6 , + v1 -> vo7 , + v1 -> vo8 , + v1 -> vo9 , + v1 -> vo10 , + v1 -> vo11 , + v1 -> vo12 , + v1 -> vo13 , + v1 -> vo14 , + v1 -> vo15 , + v1 -> vo16 , + v1 -> vo17 , + v1 -> vo18 , + v1 -> vo19 , + v1 -> vo20 , + v1 -> vo21 , + v1 -> vo22 , + v1 -> vo23 , + v1 -> vo24 , + v1 -> vo25 , + v1 -> vo26 , + v1 -> vo27 , + v1 -> vo28 , + v1 -> vo29 , + v1 -> vo30 , + v1 -> vo31 , + v1 -> vo32 +} +``` + +### Input + +### 2->1 +``` +{ + vi1 -> v1, + vi2 -> v1 +} +``` + +### 4->1 +``` +{ + vi1 -> v1, + vi2 -> v1, + vi3 -> v1, + vi4 -> v1 +} +``` + +### 8->1 +``` +{ + vi1 -> v1, + vi2 -> v1, + vi3 -> v1, + vi4 -> v1, + vi5 -> v1, + vi6 -> v1, + vi7 -> v1, + vi8 -> v1 +} +``` + +### 16->1 +``` +{ + vi1 -> v1, + vi2 -> v1, + vi3 -> v1, + vi4 -> v1, + vi5 -> v1, + vi6 -> v1, + vi7 -> v1, + vi8 -> v1, + vi9 -> v1, + vi10 -> v1, + vi11 -> v1, + vi12 -> v1, + vi13 -> v1, + vi14 -> v1, + vi15 -> v1, + vi16 -> v1 +} +``` + +### 32->1 +``` +{ + vi1 -> v1, + vi2 -> v1, + vi3 -> v1, + vi4 -> v1, + vi5 -> v1, + vi6 -> v1, + vi7 -> v1, + vi8 -> v1, + vi9 -> v1, + vi10 -> v1, + vi11 -> v1, + vi12 -> v1, + vi13 -> v1, + vi14 -> v1, + vi15 -> v1, + vi16 -> v1, + vi17 -> v1, + vi18 -> v1, + vi19 -> v1, + vi20 -> v1, + vi21 -> v1, + vi22 -> v1, + vi23 -> v1, + vi24 -> v1, + vi25 -> v1, + vi26 -> v1, + vi27 -> v1, + vi28 -> v1, + vi29 -> v1, + vi30 -> v1, + vi31 -> v1, + vi32 -> v1 +} +``` + +### Pattern based + + +``` +{ + v1:FLOW -> ?:FLOW, + (v1:FLOW -> ?:FLOW)+ +} +``` + +``` +{ + ?:FLOW -> v1:FLOW, + (?: FLOW -> v1:FLOW)+ +} +``` \ No newline at end of file diff --git a/lfr/graphmatch/figmappingmatcher.py b/lfr/graphmatch/figmappingmatcher.py index 1fef6f2..8974a4c 100644 --- a/lfr/graphmatch/figmappingmatcher.py +++ b/lfr/graphmatch/figmappingmatcher.py @@ -1,7 +1,7 @@ from typing import Dict from networkx.algorithms.isomorphism import DiGraphMatcher from networkx.classes import digraph -from reggie.nodefilter import NodeFilter +from lfr.graphmatch.nodefilter import NodeFilter from lfr.fig.fluidinteractiongraph import FluidInteractionGraph diff --git a/lfr/graphmatch/interface.py b/lfr/graphmatch/interface.py index 92e7e82..dd29031 100644 --- a/lfr/graphmatch/interface.py +++ b/lfr/graphmatch/interface.py @@ -2,8 +2,8 @@ from typing import Dict from lfr.netlistgenerator.mappinglibrary import MappingLibrary from lfr.fig.fluidinteractiongraph import FluidInteractionGraph -from reggie import MatchPattern -from reggie import NodeFilter +from lfr.graphmatch.matchpattern import MatchPattern +from lfr.graphmatch.nodefilter import NodeFilter def match_node_constraints( diff --git a/lfr/graphmatch/matchpattern.py b/lfr/graphmatch/matchpattern.py new file mode 100644 index 0000000..fafe6a4 --- /dev/null +++ b/lfr/graphmatch/matchpattern.py @@ -0,0 +1,59 @@ +from __future__ import annotations +from antlr4.CommonTokenStream import CommonTokenStream +from antlr4.tree.Tree import ParseTreeWalker +from lfr.graphmatch.nodefilter import NodeFilter +from networkx.classes.digraph import DiGraph +from typing import Dict +from antlr4 import InputStream +from lfr.antlrgen.reggie.reggieLexer import reggieLexer +from lfr.antlrgen.reggie.reggieParser import reggieParser +from lfr.graphmatch.matchpatterngenerator import MatchPatternGenerator + + +class MatchPattern: + def __init__(self, pattern_string: str = "") -> None: + + if pattern_string == "" or pattern_string is None: + raise Exception("Empty Pattern found") + + self.__pattern_string = pattern_string + self._structural_template = None + + # Dictionary that stores the nodefilter object and the node id + self._semantic_template = dict() + + self.__parse_pattern(pattern_string) + + def get_structural_template(self) -> DiGraph: + if self._structural_template is None: + raise Exception("No structural template assigned") + + return self._structural_template + + def get_semantic_template(self) -> Dict[str, NodeFilter]: + return self._semantic_template + + def __parse_pattern(self, pattern) -> None: + # Implement the reggie parser walker, execution of the compiler + # Step 1 - Parse the thing + # Step 2 - Save the structural Template + # Step 3 - Save the semantic template + istream = InputStream(pattern) + lexer = reggieLexer(istream) + stream = CommonTokenStream(lexer) + parser = reggieParser(stream) + + syntax_errors = parser.getNumberOfSyntaxErrors() + if syntax_errors > 0: + raise Exception( + "Could not parse the match expression, interrupting parsing flow" + ) + + tree = parser.graph() + walker = ParseTreeWalker() + listener = MatchPatternGenerator() + + walker.walk(listener, tree) + + self._structural_template = listener.structural_template + self._semantic_template = listener.semantic_template \ No newline at end of file diff --git a/lfr/graphmatch/matchpatterngenerator.py b/lfr/graphmatch/matchpatterngenerator.py new file mode 100644 index 0000000..fe79100 --- /dev/null +++ b/lfr/graphmatch/matchpatterngenerator.py @@ -0,0 +1,57 @@ +from lfr.graphmatch.nodefilter import NodeFilter +from lfr.antlrgen.reggie.reggieParser import reggieParser +from lfr.antlrgen.reggie.reggieListener import reggieListener +import networkx as nx + + +class MatchPatternGenerator(reggieListener): + def __init__(self) -> None: + super(MatchPatternGenerator, self).__init__() + + self.structural_template = nx.DiGraph() + self.semantic_template = dict() + self._vertices_stack = [] + + def enterVertex(self, ctx: reggieParser.VertexContext): + vertex_id = ctx.structuralid().getText() + + self._vertices_stack.append(vertex_id) + + label_filters = [] + if ctx.labelfilter() is not None: + label_filters = [label.getText() for label in ctx.labelfilter().label()] + + # TODO - Get the coloring filter + coloring_labels = [] + if ctx.coloringfilter() is not None: + coloring_labels = [s.getText() for s in ctx.coloringfilter().STRING()] + constraints_tuples = [] + for i in range(0, len(coloring_labels), 2): + constraints_tuples.append((coloring_labels[i], coloring_labels[i + 1])) + + # TODO - Put in the filter type information here + semanitc_info = NodeFilter() + + if vertex_id == "?": + raise NotImplementedError() + + else: + self.structural_template.add_node(vertex_id) + self.semantic_template[vertex_id] = NodeFilter( + node_attributes_filter=label_filters, + node_constraints=constraints_tuples, + ) + + def enterVertex2vertex(self, ctx: reggieParser.Vertex2vertexContext): + # Clear out the recent vertex memory + self._vertices_stack.clear() + + def exitVertex2vertex(self, ctx: reggieParser.Vertex2vertexContext): + # TODO - Check to see if the vertex is present in the structural template first + + # Since the vertex is in the structural template + for i in range(1, len(self._vertices_stack)): + start = self._vertices_stack[i - 1] + end = self._vertices_stack[i] + # Generate the graph + self.structural_template.add_edge(start, end) diff --git a/lfr/graphmatch/nodefilter.py b/lfr/graphmatch/nodefilter.py new file mode 100644 index 0000000..b2da47c --- /dev/null +++ b/lfr/graphmatch/nodefilter.py @@ -0,0 +1,27 @@ +from __future__ import annotations +from typing import List, Tuple + + +class NodeFilter: + def __init__( + self, + node_types_filter: List[str] = [], + node_attributes_filter: List[str] = [], + node_constraints: List[Tuple[str, str]] = [], + ) -> None: + self._node_types_filter: List[str] = node_types_filter + self._node_attributes_filter: List[str] = node_attributes_filter + self._node_constraints: List[Tuple[str, str]] = node_constraints + + def is_valid_node_type(self, type_string: str) -> bool: + if len(self._node_types_filter) == 0: + # If there are no node type filters, then return true + return True + return type_string in self._node_types_filter + + def is_valid_attribute_type(self, attributestring: str) -> bool: + # TODO - Figure out what this will be in tuture + return True + + def get_constriants(self) -> List[Tuple[str, str]]: + return self._node_constraints diff --git a/lfr/netlistgenerator/v2/constructiongraph.py b/lfr/netlistgenerator/v2/constructiongraph.py index 3f7c42b..beef18e 100644 --- a/lfr/netlistgenerator/v2/constructiongraph.py +++ b/lfr/netlistgenerator/v2/constructiongraph.py @@ -451,6 +451,7 @@ def __create_passthrough_channel( ) cn_end.input_options.remove(end_point) + # TODO - Rewrite intercn channel implementatino def __create_intercn_channel( self, src_id: str, diff --git a/reggie.g4 b/reggie.g4 new file mode 100644 index 0000000..93ddfe8 --- /dev/null +++ b/reggie.g4 @@ -0,0 +1,63 @@ +grammar reggie; + +graph: '{' graphstatement (',' graphstatement)* '}'; + +graphstatement: + basestatement + | ('(' basestatement ')' statementmodifier); + +statementmodifier: intmodifier | plusmodifier | starmodifier; + +basestatement: subgraph | vertex2vertex | vertex; + +subgraph: (ID ':') '{' vertex2vertex (',' vertex2vertex)* '}'; + +vertex: + structuralid (':' labelfilter)? coloringfilter? structuralvertexpattern*; + +coloringfilter: '{' STRING (',' STRING)* '}'; + +structuralvertexpattern: ( + intmodifier + | starmodifier + | plusmodifier + ); + +intmodifier: '[' INT ']'; + +starmodifier: '*'; + +plusmodifier: '+'; + +structuralid: (ID | QUESTIONMARK_WILDCARD); + +labelfilter: label ( '|' label)* | '(' label ( '|' label)* ')'; + +label: ID; + +vertex2vertex: vertex (edge vertex)*; + +edge: '->' | '<->'; + +ID: ('a' ..'z' | 'A' ..'Z' | '_') ( + 'a' ..'z' + | 'A' ..'Z' + | '0' ..'9' + | '_' + )*; + +//ARROW: '->'; STAR_WILDCARD: '*'; //Not used to describe any label but rather PLUS_WILDCARD: '+'; +QUESTIONMARK_WILDCARD: '?'; +WS: [ \t]+ -> skip; +NL: [\r\n]+ -> skip; // Define whitespace rule, toss it out +INT: [0-9]+; + +STRING: '"' (ESC | SAFECODEPOINT)* '"'; + +fragment HEX: [0-9a-fA-F]; + +fragment UNICODE: 'u' HEX HEX HEX HEX; + +fragment ESC: '\\' (["\\/bfnrt] | UNICODE); + +fragment SAFECODEPOINT: ~ ["\\\u0000-\u001F]; From 8103bac75cae19c79739fef4db59d665d4741194 Mon Sep 17 00:00:00 2001 From: Radhakrishna Sanka Date: Tue, 22 Jun 2021 16:25:07 -0400 Subject: [PATCH 17/36] Moved everything out of v2 --- generator-old.py | 22 +++---- lfr/cmdline.py | 4 +- .../{v2 => }/connectingoption.py | 0 .../{v2 => }/constructiongraph.py | 4 +- .../{v2 => }/constructionnode.py | 9 +-- .../{v2/dafdadapter.py => dafdadapter-old.py} | 58 +++++++++-------- lfr/netlistgenerator/dafdadapter.py | 58 +++++++---------- .../{v2 => }/explicitmappingoption.py | 7 +-- .../{v2 => }/gen_strategies/dropxstrategy.py | 62 ++++++++++++------- .../{v2 => }/gen_strategies/dummy.py | 6 +- .../{v2 => }/gen_strategies/genstrategy.py | 10 ++- .../{v2 => }/gen_strategies/mars.py | 6 +- .../{v2 => }/gen_strategies/marsstrategy.py | 12 ++-- lfr/netlistgenerator/{v2 => }/generator.py | 40 ++++++------ .../{v2 => }/mappingoption.py | 0 .../{v2 => }/networkmappingoption.py | 2 +- .../packaged_libraries/dropx.py | 2 +- lfr/netlistgenerator/primitive.py | 28 ++++++--- .../gradient_generator.py | 14 +++-- .../procedural_component_algorithms/mux.py | 5 +- .../procedural_component_algorithms/mux3d.py | 11 ++-- .../transposer.py | 2 +- .../procedural_component_algorithms/tree.py | 2 +- .../procedural_component_algorithms/ytree.py | 2 +- lfr/netlistgenerator/v2/__init__.py | 0 25 files changed, 197 insertions(+), 169 deletions(-) rename lfr/netlistgenerator/{v2 => }/connectingoption.py (100%) rename lfr/netlistgenerator/{v2 => }/constructiongraph.py (99%) rename lfr/netlistgenerator/{v2 => }/constructionnode.py (94%) rename lfr/netlistgenerator/{v2/dafdadapter.py => dafdadapter-old.py} (53%) rename lfr/netlistgenerator/{v2 => }/explicitmappingoption.py (85%) rename lfr/netlistgenerator/{v2 => }/gen_strategies/dropxstrategy.py (89%) rename lfr/netlistgenerator/{v2 => }/gen_strategies/dummy.py (68%) rename lfr/netlistgenerator/{v2 => }/gen_strategies/genstrategy.py (94%) rename lfr/netlistgenerator/{v2 => }/gen_strategies/mars.py (77%) rename lfr/netlistgenerator/{v2 => }/gen_strategies/marsstrategy.py (94%) rename lfr/netlistgenerator/{v2 => }/generator.py (98%) rename lfr/netlistgenerator/{v2 => }/mappingoption.py (100%) rename lfr/netlistgenerator/{v2 => }/networkmappingoption.py (92%) rename lfr/netlistgenerator/{v2 => }/procedural_component_algorithms/gradient_generator.py (90%) rename lfr/netlistgenerator/{v2 => }/procedural_component_algorithms/mux.py (95%) rename lfr/netlistgenerator/{v2 => }/procedural_component_algorithms/mux3d.py (92%) rename lfr/netlistgenerator/{v2 => }/procedural_component_algorithms/transposer.py (97%) rename lfr/netlistgenerator/{v2 => }/procedural_component_algorithms/tree.py (97%) rename lfr/netlistgenerator/{v2 => }/procedural_component_algorithms/ytree.py (98%) delete mode 100644 lfr/netlistgenerator/v2/__init__.py diff --git a/generator-old.py b/generator-old.py index 1f4d6ab..e230e29 100644 --- a/generator-old.py +++ b/generator-old.py @@ -1,29 +1,29 @@ -from lfr.netlistgenerator.v2.procedural_component_algorithms.ytree import YTREE -from lfr.netlistgenerator.v2.gen_strategies.dropxstrategy import DropXStrategy +from lfr.netlistgenerator.procedural_component_algorithms.ytree import YTREE +from lfr.netlistgenerator.gen_strategies.dropxstrategy import DropXStrategy from lfr.fig.fluidinteractiongraph import FluidInteractionGraph from lfr.postprocessor.mapping import NetworkMapping, NodeMappingTemplate from pymint.mintlayer import MINTLayerType from lfr.netlistgenerator.primitive import NetworkPrimitive, Primitive, PrimitiveType -from lfr.netlistgenerator.v2.connectingoption import ConnectingOption +from lfr.netlistgenerator.connectingoption import ConnectingOption from lfr.netlistgenerator.mappinglibrary import MappingLibrary -from lfr.netlistgenerator.v2.networkmappingoption import ( +from lfr.netlistgenerator.networkmappingoption import ( NetworkMappingOption, NetworkMappingOptionType, ) -from lfr.netlistgenerator.v2.gen_strategies.genstrategy import GenStrategy +from lfr.netlistgenerator.gen_strategies.genstrategy import GenStrategy from lfr.fig.fignode import IOType, ValueNode from typing import List from pymint.mintdevice import MINTDevice from lfr.netlistgenerator.namegenerator import NameGenerator -from lfr.netlistgenerator.v2.gen_strategies.dummy import DummyStrategy -from lfr.netlistgenerator.v2.constructionnode import ConstructionNode -from lfr.netlistgenerator.v2.constructiongraph import ConstructionGraph +from lfr.netlistgenerator.gen_strategies.dummy import DummyStrategy +from lfr.netlistgenerator.constructionnode import ConstructionNode +from lfr.netlistgenerator.constructiongraph import ConstructionGraph from lfr.fig.interaction import ( FluidIntegerInteraction, FluidNumberInteraction, InteractionType, ) -from lfr.netlistgenerator.v2.mappingoption import MappingOption +from lfr.netlistgenerator.mappingoption import MappingOption from lfr.compiler.module import Module import networkx as nx @@ -853,8 +853,8 @@ def eliminate_passthrough_nodes(construction_graph: ConstructionGraph): construction_graph.add_edge(in_point, out_point) else: raise Exception( - "Pass through network node elimination not implemented \ - when n->n edge creation is necessary" + "Pass through network node elimination not implemented " + " when n->n edge creation is necessary" ) diff --git a/lfr/cmdline.py b/lfr/cmdline.py index e34977e..135e79d 100644 --- a/lfr/cmdline.py +++ b/lfr/cmdline.py @@ -13,14 +13,14 @@ from lfr.antlrgen.lfr.lfrXLexer import lfrXLexer from lfr.antlrgen.lfr.lfrXParser import lfrXParser from lfr.netlistgenerator.mappinglibrary import MappingLibrary -from lfr.netlistgenerator.v2.generator import ( +from lfr.netlistgenerator.generator import ( + generate_dropx_library, generate, generate_dropx_library, generate_mars_library, ) from lfr.utils import print_netlist, printgraph, serialize_netlist from lfr.preprocessor import PreProcessor -from lfr.postProcessListener import PostProcessListener def load_libraries(): diff --git a/lfr/netlistgenerator/v2/connectingoption.py b/lfr/netlistgenerator/connectingoption.py similarity index 100% rename from lfr/netlistgenerator/v2/connectingoption.py rename to lfr/netlistgenerator/connectingoption.py diff --git a/lfr/netlistgenerator/v2/constructiongraph.py b/lfr/netlistgenerator/constructiongraph.py similarity index 99% rename from lfr/netlistgenerator/v2/constructiongraph.py rename to lfr/netlistgenerator/constructiongraph.py index beef18e..dbd6489 100644 --- a/lfr/netlistgenerator/v2/constructiongraph.py +++ b/lfr/netlistgenerator/constructiongraph.py @@ -14,8 +14,8 @@ from lfr.fig.fluidinteractiongraph import FluidInteractionGraph from lfr.netlistgenerator.namegenerator import NameGenerator from lfr.netlistgenerator.primitive import PrimitiveType, ProceduralPrimitive -from lfr.netlistgenerator.v2.constructionnode import ConstructionNode -from lfr.netlistgenerator.v2.networkmappingoption import ( +from lfr.netlistgenerator.constructionnode import ConstructionNode +from lfr.netlistgenerator.networkmappingoption import ( NetworkMappingOption, NetworkMappingOptionType, ) diff --git a/lfr/netlistgenerator/v2/constructionnode.py b/lfr/netlistgenerator/constructionnode.py similarity index 94% rename from lfr/netlistgenerator/v2/constructionnode.py rename to lfr/netlistgenerator/constructionnode.py index 119f9b5..4811306 100644 --- a/lfr/netlistgenerator/v2/constructionnode.py +++ b/lfr/netlistgenerator/constructionnode.py @@ -1,10 +1,7 @@ -import copy -from typing import List - -from lfr.netlistgenerator.primitive import ProceduralPrimitive -from lfr.netlistgenerator.v2.connectingoption import ConnectingOption -from lfr.netlistgenerator.v2.mappingoption import MappingOption from lfr.postprocessor.constraints import Constraint +from lfr.netlistgenerator.connectingoption import ConnectingOption +from lfr.netlistgenerator.mappingoption import MappingOption +from typing import List class ConstructionNode: diff --git a/lfr/netlistgenerator/v2/dafdadapter.py b/lfr/netlistgenerator/dafdadapter-old.py similarity index 53% rename from lfr/netlistgenerator/v2/dafdadapter.py rename to lfr/netlistgenerator/dafdadapter-old.py index a9e7922..1aab89f 100644 --- a/lfr/netlistgenerator/v2/dafdadapter.py +++ b/lfr/netlistgenerator/dafdadapter-old.py @@ -1,42 +1,39 @@ -from typing import List - -from pymint.mintcomponent import MINTComponent +from lfr.postprocessor.constraints import ( + ConstraintList, + FunctionalConstraint, + GeometryConstraint, + PerformanceConstraint, +) from pymint.mintdevice import MINTDevice - -from lfr.postprocessor.constraints import Constraint +from dafd import DAFD_Interface -class DAFDAdapter: +class DAFDSizingAdapter: def __init__(self, device: MINTDevice) -> None: - super().__init__() - self._device = device - - def size_droplet_generator( - self, component: MINTComponent, constriants: List[Constraint] - ) -> None: - import faulthandler - - faulthandler.enable() - - from dafd import DAFD_Interface - + self.__device = device self.solver = DAFD_Interface() + + def size_droplet_generator(self, constriants: ConstraintList) -> None: # TODO: Check the type of the component and pull info from DAFD Interface targets_dict = {} constriants_dict = {} for constraint in constriants: - if constraint.key == "volume": - # r≈0.62035V1/3 - volume = constraint.get_target_value() - targets_dict["droplet_size"] = float(volume) ** 0.33 * 0.62035 * 2 - elif constraint.key == "generation_rate": - generate_rate = constraint.get_target_value() - targets_dict["generation_rate"] = generate_rate - else: + if isinstance(constraint, FunctionalConstraint): + if constraint.key == "volume": + # r≈0.62035V1/3 + volume = constraint.get_target_value() + assert volume is not None + targets_dict["droplet_size"] = float(volume) ** 0.33 * 0.62035 * 2 + elif isinstance(constraint, PerformanceConstraint): + if constraint.key == "generation_rate": + generate_rate = constraint.get_target_value() + targets_dict["generation_rate"] = generate_rate + elif isinstance(constraint, GeometryConstraint): raise Exception("Error: Geometry constraint not defined") results = self.solver.runInterp(targets_dict, constriants_dict) + component = constriants.component if component is None: raise Exception("No component attached to the constraints") @@ -62,3 +59,12 @@ def size_droplet_generator( component.params.set_param("outputWidth", round(orifice_size * expansion_ratio)) component.params.set_param("outputLength", 5000) component.params.set_param("height", round(orifice_size / aspect_ratio)) + + # TODO: Figure out how to propagate the results to the rest of the design. Additionally we need to set all the operation considtions + pass + + def size_component(self, constriants: ConstraintList) -> None: + if constriants.component.entity == "NOZZLE DROPLET GENERATOR": + self.size_droplet_generator(constriants) + else: + print("Error: {} is not supported".format(constriants.component.entity)) diff --git a/lfr/netlistgenerator/dafdadapter.py b/lfr/netlistgenerator/dafdadapter.py index e7cc240..b7a2f9c 100644 --- a/lfr/netlistgenerator/dafdadapter.py +++ b/lfr/netlistgenerator/dafdadapter.py @@ -1,40 +1,40 @@ -from lfr.postprocessor.constraints import ( - ConstraintList, - FunctionalConstraint, - GeometryConstraint, - PerformanceConstraint, -) -from pymint.mintdevice import MINTDevice -from dafd import DAFD_Interface +from lfr.postprocessor.constraints import Constraint +from typing import List from pymint.mintcomponent import MINTComponent +from pymint.mintdevice import MINTDevice -class DAFDSizingAdapter: +class DAFDAdapter: def __init__(self, device: MINTDevice) -> None: - self.__device = device - self.solver = DAFD_Interface() + super().__init__() + self._device = device + + def size_droplet_generator( + self, component: MINTComponent, constriants: List[Constraint] + ) -> None: + import faulthandler + + faulthandler.enable() - def size_droplet_generator(self, constriants: ConstraintList) -> None: + from dafd import DAFD_Interface + + self.solver = DAFD_Interface() # TODO: Check the type of the component and pull info from DAFD Interface targets_dict = {} constriants_dict = {} for constraint in constriants: - if isinstance(constraint, FunctionalConstraint): - if constraint.key == "volume": - # r≈0.62035V1/3 - volume = constraint.get_target_value() - assert volume is not None - targets_dict["droplet_size"] = float(volume) ** 0.33 * 0.62035 * 2 - elif isinstance(constraint, PerformanceConstraint): - if constraint.key == "generation_rate": - generate_rate = constraint.get_target_value() - targets_dict["generation_rate"] = generate_rate - elif isinstance(constraint, GeometryConstraint): + if constraint.key == "volume": + # r≈0.62035V1/3 + volume = constraint.get_target_value() + targets_dict["droplet_size"] = float(volume) ** 0.33 * 0.62035 * 2 + elif constraint.key == "generation_rate": + generate_rate = constraint.get_target_value() + targets_dict["generation_rate"] = generate_rate + else: raise Exception("Error: Geometry constraint not defined") results = self.solver.runInterp(targets_dict, constriants_dict) - component = constriants.component if component is None: raise Exception("No component attached to the constraints") @@ -60,13 +60,3 @@ def size_droplet_generator(self, constriants: ConstraintList) -> None: component.params.set_param("outputWidth", round(orifice_size * expansion_ratio)) component.params.set_param("outputLength", 5000) component.params.set_param("height", round(orifice_size / aspect_ratio)) - - # TODO: Figure out how to propagate the results to the rest of the design. - # Additionally we need to set all the operation considtions - pass - - def size_component(self, constriants: ConstraintList) -> None: - if constriants.component.entity == "NOZZLE DROPLET GENERATOR": - self.size_droplet_generator(constriants) - else: - print("Error: {} is not supported".format(constriants.component.entity)) diff --git a/lfr/netlistgenerator/v2/explicitmappingoption.py b/lfr/netlistgenerator/explicitmappingoption.py similarity index 85% rename from lfr/netlistgenerator/v2/explicitmappingoption.py rename to lfr/netlistgenerator/explicitmappingoption.py index 295272e..43a8091 100644 --- a/lfr/netlistgenerator/v2/explicitmappingoption.py +++ b/lfr/netlistgenerator/explicitmappingoption.py @@ -1,8 +1,7 @@ -from typing import List - -from lfr.netlistgenerator.v2.mappingoption import MappingOption -from lfr.netlistgenerator.v2.networkmappingoption import NetworkMappingOption from lfr.postprocessor.constraints import Constraint +from typing import List +from lfr.netlistgenerator.mappingoption import MappingOption +from lfr.netlistgenerator.networkmappingoption import NetworkMappingOption class ExplicitMappingOption: diff --git a/lfr/netlistgenerator/v2/gen_strategies/dropxstrategy.py b/lfr/netlistgenerator/gen_strategies/dropxstrategy.py similarity index 89% rename from lfr/netlistgenerator/v2/gen_strategies/dropxstrategy.py rename to lfr/netlistgenerator/gen_strategies/dropxstrategy.py index 6ba2b5e..9f82561 100644 --- a/lfr/netlistgenerator/v2/gen_strategies/dropxstrategy.py +++ b/lfr/netlistgenerator/gen_strategies/dropxstrategy.py @@ -1,13 +1,12 @@ +from lfr.fig.interaction import Interaction, InteractionType +from lfr.netlistgenerator.dafdadapter import DAFDAdapter +from lfr.fig.fluidinteractiongraph import FluidInteractionGraph +from lfr.netlistgenerator.constructiongraph import ConstructionGraph +from lfr.netlistgenerator.constructionnode import ConstructionNode +from lfr.netlistgenerator.gen_strategies.genstrategy import GenStrategy import networkx as nx from pymint import MINTDevice -from lfr.fig.fluidinteractiongraph import FluidInteractionGraph -from lfr.fig.interaction import Interaction, InteractionType -from lfr.netlistgenerator.v2.constructiongraph import ConstructionGraph -from lfr.netlistgenerator.v2.constructionnode import ConstructionNode -from lfr.netlistgenerator.v2.dafdadapter import DAFDAdapter -from lfr.netlistgenerator.v2.gen_strategies.genstrategy import GenStrategy - class DropXStrategy(GenStrategy): def __init__( @@ -27,19 +26,25 @@ def reduce_mapping_options(self) -> None: if ConstructionNode(fignode.id).is_explictly_mapped: pass # TODO - Implement Generalized Ali Strategy 1 - # Rule 1 - The first level of % should be mapping to a Droplet Generator (TODO - Talk to ali about this or a T junction generator) - # Step 1 - Check if fig node is interaction and of type METER (%) this is the check condition for rule 1 + # Rule 1 - The first level of % should be mapping to a Droplet Generator + # (TODO - Talk to ali about this or a T junction generator) + # Step 1 - Check if fig node is interaction and of type METER (%) this is t + # he check condition for rule 1 else: if isinstance(fignode, Interaction): if fignode.type is InteractionType.METER: is_first_metering_node = True - # TODO - Check if DROPLET GENERATOR is one of the mapping options, skip if not - # Step 2 - If type is meter then check to see if other it is the first meter operation from inputs to current fignode + # TODO - Check if DROPLET GENERATOR is one of the mapping + # options, skip if not + # Step 2 - If type is meter then check to see if other it is + # the first meter operation from inputs to current fignode for sorted_fignode_id in figs_in_order: if fignode_id == sorted_fignode_id: # End of checking if is_first_metering_node is True: - # TODO - Eliminate all options other than Nozzle droplet generator for the associated construction node(s) + # TODO - Eliminate all options other than Nozzle + # droplet generator for the associated construction + # node(s) cn = self._construction_graph.get_fignode_cn( fignode ) @@ -64,13 +69,15 @@ def reduce_mapping_options(self) -> None: ) if isinstance(sorted_fignode, Interaction): if sorted_fignode.type is InteractionType.METER: - # check if node is connected to our node of interest + # check if node is connected to our node of + # interest if sorted_fignode_id in self._fig.predecessors( fignode_id ): is_first_metering_node = False - # Rule 2 – Any +-, distribute nodes before % should be in continuous flow (figure out components for this) + # Rule 2 – Any +-, distribute nodes before % should be in continuous flow + # (figure out components for this) # TODO - Go through each of the FIG nodes, if the fig node has for fignode_id in self._fig.nodes: @@ -87,9 +94,12 @@ def reduce_mapping_options(self) -> None: # this is NOT continuous raise Exception("flow before METER is not continuous") - # Rule 3 – Any Remetering (%) should require a droplet breakdown and regeneration (Ask Ali) - # Rule 4 – Distribute network post Metering stage should be mapped to different kinds of separator / selection/ storage networks - # Rule 5 – If plus is shown between node that has % in pred and non % in pred, then its pico injection + # Rule 3 – Any Remetering (%) should require a droplet breakdown and + # regeneration (Ask Ali) + # Rule 4 – Distribute network post Metering stage should be mapped to different + # kinds of separator / selection/ storage networks + # Rule 5 – If plus is shown between node that has % in pred and non % in pred, t + # hen its pico injection for fignode_id in self._fig.nodes: fignode = self._fig.get_fignode(fignode_id) @@ -168,7 +178,8 @@ def reduce_mapping_options(self) -> None: print("-", cn_part.primitive.mint) pass - # Rule 6 – if plus is sown between two nodes that has % in pred, then its droplet merging + # Rule 6 – if plus is sown between two nodes that has % in pred, then its + # droplet merging # Rule 7 – TBD Rule for droplet splitting # Finally just reduce the total number of mapping options if greater than 1 super().reduce_mapping_options() @@ -191,10 +202,12 @@ def __exist_in_cn(cn, mint_name): return False def __search_predecessors(self, fignode_id, search_type): - """recursive function searches for the specified InteractionType in the predecessors of the specified fignode_id + """recursive function searches for the specified InteractionType in the + predecessors of the specified fignode_id Args: - fignode_id (elements in self._fig.nodes): Starting node to find the predecessors + fignode_id (elements in self._fig.nodes): Starting node to find the + predecessors search_type (InteractionType): Interaction type to find in the predecessors Returns: @@ -214,7 +227,8 @@ def __search_predecessors(self, fignode_id, search_type): @staticmethod def __check_if_type(fignode, search_type): - """helper function for __search_predecessors and __check_continuous. Check if the specified search_type matches to the fignode.type + """helper function for __search_predecessors and __check_continuous. Check if + the specified search_type matches to the fignode.type Args: fignode (FIGNode): fignode that contains InteractionType as type @@ -229,7 +243,8 @@ def __check_if_type(fignode, search_type): return False - # this function checks if the predecessors before METER has METER or not. If not, continuous. + # this function checks if the predecessors before METER has METER or not. If not, + # continuous. def __check_continuous(self, fignode_id): """recursive function to check if the flow before METER is continuous at MIX or SIEVE @@ -237,7 +252,8 @@ def __check_continuous(self, fignode_id): fignode_id (elements in self._fig.nodes): Starting node to find predecessors Returns: - Bool: if METER is found in the predecessors of METER, returns true (not continuous), Else false + Bool: if METER is found in the predecessors of METER, returns true + (not continuous), Else false """ fignode = self._fig.get_fignode(fignode_id) diff --git a/lfr/netlistgenerator/v2/gen_strategies/dummy.py b/lfr/netlistgenerator/gen_strategies/dummy.py similarity index 68% rename from lfr/netlistgenerator/v2/gen_strategies/dummy.py rename to lfr/netlistgenerator/gen_strategies/dummy.py index e5935fe..6011a3b 100644 --- a/lfr/netlistgenerator/v2/gen_strategies/dummy.py +++ b/lfr/netlistgenerator/gen_strategies/dummy.py @@ -1,7 +1,7 @@ from lfr.fig.fluidinteractiongraph import FluidInteractionGraph -from lfr.netlistgenerator.v2.constructiongraph import ConstructionGraph -from lfr.netlistgenerator.v2.gen_strategies.genstrategy import GenStrategy -from lfr.netlistgenerator.v2.mappingoption import MappingOption +from lfr.netlistgenerator.mappingoption import MappingOption +from lfr.netlistgenerator.gen_strategies.genstrategy import GenStrategy +from lfr.netlistgenerator.constructiongraph import ConstructionGraph class DummyStrategy(GenStrategy): diff --git a/lfr/netlistgenerator/v2/gen_strategies/genstrategy.py b/lfr/netlistgenerator/gen_strategies/genstrategy.py similarity index 94% rename from lfr/netlistgenerator/v2/gen_strategies/genstrategy.py rename to lfr/netlistgenerator/gen_strategies/genstrategy.py index b554d23..3c72032 100644 --- a/lfr/netlistgenerator/v2/gen_strategies/genstrategy.py +++ b/lfr/netlistgenerator/gen_strategies/genstrategy.py @@ -7,16 +7,15 @@ from lfr.fig.fluidinteractiongraph import FluidInteractionGraph if TYPE_CHECKING: - from lfr.netlistgenerator.v2.constructiongraph import ConstructionGraph + from lfr.netlistgenerator.constructiongraph import ConstructionGraph +from lfr.netlistgenerator.connectingoption import ConnectingOption from typing import List from pymint.mintdevice import MINTDevice from pymint.mintnode import MINTNode from pymint.minttarget import MINTTarget -from lfr.netlistgenerator.v2.connectingoption import ConnectingOption - class GenStrategy: def __init__( @@ -33,9 +32,8 @@ def reduce_mapping_options(self) -> None: # clean this # Remove the extra mappings print( - "Reducing mapping options for Construction node: {} from {} to {}".format( - cn.id, len(cn.mapping_options), 1 - ), + "Reducing mapping options for Construction node: {} from {} to {}" + .format(cn.id, len(cn.mapping_options), 1), ) if len(cn.mapping_options) > 1: for option in cn.mapping_options: diff --git a/lfr/netlistgenerator/v2/gen_strategies/mars.py b/lfr/netlistgenerator/gen_strategies/mars.py similarity index 77% rename from lfr/netlistgenerator/v2/gen_strategies/mars.py rename to lfr/netlistgenerator/gen_strategies/mars.py index b21135d..fa33a12 100644 --- a/lfr/netlistgenerator/v2/gen_strategies/mars.py +++ b/lfr/netlistgenerator/gen_strategies/mars.py @@ -1,7 +1,5 @@ -from typing import overload - -from lfr.netlistgenerator.v2.constructiongraph import ConstructionGraph -from lfr.netlistgenerator.v2.gen_strategies.genstrategy import GenStrategy +from lfr.netlistgenerator.constructiongraph import ConstructionGraph +from lfr.netlistgenerator.gen_strategies.genstrategy import GenStrategy class MARSStrategy(GenStrategy): diff --git a/lfr/netlistgenerator/v2/gen_strategies/marsstrategy.py b/lfr/netlistgenerator/gen_strategies/marsstrategy.py similarity index 94% rename from lfr/netlistgenerator/v2/gen_strategies/marsstrategy.py rename to lfr/netlistgenerator/gen_strategies/marsstrategy.py index 21d52cc..fe426e4 100644 --- a/lfr/netlistgenerator/v2/gen_strategies/marsstrategy.py +++ b/lfr/netlistgenerator/gen_strategies/marsstrategy.py @@ -6,13 +6,17 @@ from pymint.mintlayer import MINTLayerType from lfr.fig.fluidinteractiongraph import FluidInteractionGraph -from lfr.fig.interaction import Interaction -from lfr.netlistgenerator.v2.constructionnode import ConstructionNode -from lfr.netlistgenerator.v2.dafdadapter import DAFDAdapter +from lfr.netlistgenerator.dafdadapter import DAFDAdapter +from lfr.netlistgenerator.constructionnode import ConstructionNode + +from typing import Dict, TYPE_CHECKING + +from pymint.mintlayer import MINTLayerType if TYPE_CHECKING: - from lfr.netlistgenerator.v2.constructiongraph import ConstructionGraph + from lfr.netlistgenerator.constructiongraph import ConstructionGraph +from lfr.netlistgenerator.connectingoption import ConnectingOption from typing import List from pymint.mintdevice import MINTDevice diff --git a/lfr/netlistgenerator/v2/generator.py b/lfr/netlistgenerator/generator.py similarity index 98% rename from lfr/netlistgenerator/v2/generator.py rename to lfr/netlistgenerator/generator.py index 26fb330..d1d0bc0 100644 --- a/lfr/netlistgenerator/v2/generator.py +++ b/lfr/netlistgenerator/generator.py @@ -6,33 +6,37 @@ from pymint.mintdevice import MINTDevice from lfr.graphmatch.interface import get_fig_matches +from lfr.netlistgenerator.procedural_component_algorithms.ytree import YTREE +from lfr.netlistgenerator.gen_strategies.dropxstrategy import DropXStrategy +from lfr.netlistgenerator.gen_strategies.marsstrategy import MarsStrategy +from lfr.fig.fluidinteractiongraph import FluidInteractionGraph +from lfr.postprocessor.mapping import NetworkMapping, NodeMappingTemplate from pymint.mintlayer import MINTLayerType -from pymint import MINTDevice - -from lfr.compiler.module import Module +from lfr.netlistgenerator.primitive import NetworkPrimitive, Primitive, PrimitiveType +from lfr.netlistgenerator.connectingoption import ConnectingOption +from lfr.netlistgenerator.mappinglibrary import MappingLibrary +from lfr.netlistgenerator.networkmappingoption import ( + NetworkMappingOption, + NetworkMappingOptionType, +) +from lfr.netlistgenerator.gen_strategies.genstrategy import GenStrategy from lfr.fig.fignode import IOType, Pump, Storage, ValueNode +from lfr.netlistgenerator.namegenerator import NameGenerator +from lfr.netlistgenerator.gen_strategies.dummy import DummyStrategy +from lfr.netlistgenerator.constructionnode import ConstructionNode +from lfr.netlistgenerator.constructiongraph import ConstructionGraph from lfr.fig.interaction import ( FluidIntegerInteraction, FluidNumberInteraction, InteractionType, ) -from lfr.netlistgenerator.mappinglibrary import MappingLibrary -from lfr.netlistgenerator.namegenerator import NameGenerator -from lfr.netlistgenerator.primitive import NetworkPrimitive, Primitive, PrimitiveType -from lfr.netlistgenerator.v2.connectingoption import ConnectingOption -from lfr.netlistgenerator.v2.constructiongraph import ConstructionGraph -from lfr.netlistgenerator.v2.constructionnode import ConstructionNode -from lfr.netlistgenerator.v2.gen_strategies.dummy import DummyStrategy -from lfr.netlistgenerator.v2.gen_strategies.genstrategy import GenStrategy -from lfr.netlistgenerator.v2.gen_strategies.marsstrategy import MarsStrategy -from lfr.netlistgenerator.v2.mappingoption import MappingOption -from lfr.netlistgenerator.v2.networkmappingoption import ( - NetworkMappingOption, - NetworkMappingOptionType, -) +from lfr.netlistgenerator.mappingoption import MappingOption +from lfr.compiler.module import Module + # def generate_MARS_library() -> MappingLibrary: -# # TODO - Programatically create each of the items necessary for the MARS primitive library, +# # TODO - Programatically create each of the items necessary for the MARS +# primitive library, # # we shall serialize them after experimentation # # mix_primitive = MappingOption() diff --git a/lfr/netlistgenerator/v2/mappingoption.py b/lfr/netlistgenerator/mappingoption.py similarity index 100% rename from lfr/netlistgenerator/v2/mappingoption.py rename to lfr/netlistgenerator/mappingoption.py diff --git a/lfr/netlistgenerator/v2/networkmappingoption.py b/lfr/netlistgenerator/networkmappingoption.py similarity index 92% rename from lfr/netlistgenerator/v2/networkmappingoption.py rename to lfr/netlistgenerator/networkmappingoption.py index 556732e..c3de245 100644 --- a/lfr/netlistgenerator/v2/networkmappingoption.py +++ b/lfr/netlistgenerator/networkmappingoption.py @@ -1,7 +1,7 @@ from enum import Enum from lfr.netlistgenerator.primitive import NetworkPrimitive -from lfr.netlistgenerator.v2.mappingoption import MappingOption +from lfr.netlistgenerator.mappingoption import MappingOption class NetworkMappingOptionType(Enum): diff --git a/lfr/netlistgenerator/packaged_libraries/dropx.py b/lfr/netlistgenerator/packaged_libraries/dropx.py index ed4732e..2a014d8 100644 --- a/lfr/netlistgenerator/packaged_libraries/dropx.py +++ b/lfr/netlistgenerator/packaged_libraries/dropx.py @@ -1,7 +1,7 @@ from lfr.fig.interaction import InteractionType from lfr.netlistgenerator.mappinglibrary import MappingLibrary from lfr.netlistgenerator.primitive import Primitive, PrimitiveType -from lfr.netlistgenerator.v2.connectingoption import ConnectingOption +from lfr.netlistgenerator.connectingoption import ConnectingOption def generate_dropx_library() -> MappingLibrary: diff --git a/lfr/netlistgenerator/primitive.py b/lfr/netlistgenerator/primitive.py index 41daa01..7162d27 100644 --- a/lfr/netlistgenerator/primitive.py +++ b/lfr/netlistgenerator/primitive.py @@ -7,10 +7,10 @@ from pymint.mintlayer import MINTLayer from pymint.mintparams import MINTParams +from lfr.netlistgenerator.gen_strategies.genstrategy import GenStrategy +from lfr.netlistgenerator.connectingoption import ConnectingOption from lfr import parameters from lfr.netlistgenerator.namegenerator import NameGenerator -from lfr.netlistgenerator.v2.connectingoption import ConnectingOption -from lfr.netlistgenerator.v2.gen_strategies.genstrategy import GenStrategy class PrimitiveType(Enum): @@ -124,8 +124,10 @@ def get_default_netlist(self, cn_id: str, name_gen: NameGenerator) -> MINTDevice """Returns the default netlist for the primitive Args: - cn_id (str): ID of the construction node so that we can prefix the id's of all the components that are part of the default netlist - name_gen (NameGenerator): A namegenerator instance that is used for the globally for synthesizing the design + cn_id (str): ID of the construction node so that we can prefix the id's of + all the components that are part of the default netlist + name_gen (NameGenerator): A namegenerator instance that is used for the + globally for synthesizing the design Returns: MINTDevice: Default netlist of whatever the primitive is @@ -207,7 +209,8 @@ def generate_input_connectingoptions(self, subgraph_view) -> List[ConnectingOpti be connected to the primitive Args: - subgraph_view (networkx.Graph.subgraph): A subgraph view of the Fluid Interaction Graph + subgraph_view (networkx.Graph.subgraph): A subgraph view of the Fluid + Interaction Graph Raises: NotImplementedError: Raised when its not implemented @@ -224,7 +227,8 @@ def generate_output_connectingoptions( be connected to the primitive Args: - subgraph_view (networkx.Graph.subgraph): A subgraph view of the Fluid Interaction Graph + subgraph_view (networkx.Graph.subgraph): A subgraph view of the Fluid + Interaction Graph Raises: @@ -242,7 +246,8 @@ def generate_carrier_connectingoptions( be connected to the primitive Args: - subgraph_view (networkx.Graph.subgraph): A subgraph view of the Fluid Interaction Graph + subgraph_view (networkx.Graph.subgraph): A subgraph view of the Fluid + Interaction Graph Raises: NotImplementedError: Raised when its not implemented @@ -259,7 +264,8 @@ def generate_loading_connectingoptions( be connected to the primitive Args: - subgraph_view (networkx.Graph.subgraph): A subgraph view of the Fluid Interaction Graph + subgraph_view (networkx.Graph.subgraph): A subgraph view of the Fluid + Interaction Graph Raises: NotImplementedError: Raised when its not implemented @@ -314,8 +320,10 @@ def get_default_netlist(self, cn_id: str, name_gen: NameGenerator) -> MINTDevice """Returns the default netlist for the primitive Args: - cn_id (str): ID of the construction node so that we can prefix the id's of all the components that are part of the default netlist - name_gen (NameGenerator): A namegenerator instance that is used for the globally for synthesizing the design + cn_id (str): ID of the construction node so that we can prefix the id's of + all the components that are part of the default netlist + name_gen (NameGenerator): A namegenerator instance that is used for the + globally for synthesizing the design Raises: Exception: Raised when there is no defualt netlist is generated diff --git a/lfr/netlistgenerator/v2/procedural_component_algorithms/gradient_generator.py b/lfr/netlistgenerator/procedural_component_algorithms/gradient_generator.py similarity index 90% rename from lfr/netlistgenerator/v2/procedural_component_algorithms/gradient_generator.py rename to lfr/netlistgenerator/procedural_component_algorithms/gradient_generator.py index a1c41b6..939aa83 100644 --- a/lfr/netlistgenerator/v2/procedural_component_algorithms/gradient_generator.py +++ b/lfr/netlistgenerator/procedural_component_algorithms/gradient_generator.py @@ -1,7 +1,7 @@ +from lfr.netlistgenerator.connectingoption import ConnectingOption from typing import List from lfr.netlistgenerator.primitive import ProceduralPrimitive -from lfr.netlistgenerator.v2.connectingoption import ConnectingOption class GRADIENTGENERATOR(ProceduralPrimitive): @@ -18,7 +18,8 @@ def generate_input_connectingoptions(self, subgraph_view) -> List[ConnectingOpti be connected to the primitive Args: - subgraph_view (networkx.Graph.subgraph): A subgraph view of the Fluid Interaction Graph + subgraph_view (networkx.Graph.subgraph): A subgraph view of the Fluid + Interaction Graph Raises: NotImplementedError: Raised when its not implemented @@ -35,7 +36,8 @@ def generate_output_connectingoptions( be connected to the primitive Args: - subgraph_view (networkx.Graph.subgraph): A subgraph view of the Fluid Interaction Graph + subgraph_view (networkx.Graph.subgraph): A subgraph view of the Fluid + Interaction Graph Raises: @@ -53,7 +55,8 @@ def generate_carrier_connectingoptions( be connected to the primitive Args: - subgraph_view (networkx.Graph.subgraph): A subgraph view of the Fluid Interaction Graph + subgraph_view (networkx.Graph.subgraph): A subgraph view of the Fluid + Interaction Graph Raises: NotImplementedError: Raised when its not implemented @@ -70,7 +73,8 @@ def generate_loading_connectingoptions( be connected to the primitive Args: - subgraph_view (networkx.Graph.subgraph): A subgraph view of the Fluid Interaction Graph + subgraph_view (networkx.Graph.subgraph): A subgraph view of the Fluid + Interaction Graph Raises: NotImplementedError: Raised when its not implemented diff --git a/lfr/netlistgenerator/v2/procedural_component_algorithms/mux.py b/lfr/netlistgenerator/procedural_component_algorithms/mux.py similarity index 95% rename from lfr/netlistgenerator/v2/procedural_component_algorithms/mux.py rename to lfr/netlistgenerator/procedural_component_algorithms/mux.py index 2b1937a..f748022 100644 --- a/lfr/netlistgenerator/v2/procedural_component_algorithms/mux.py +++ b/lfr/netlistgenerator/procedural_component_algorithms/mux.py @@ -1,7 +1,7 @@ +from lfr.netlistgenerator.connectingoption import ConnectingOption from typing import List from lfr.netlistgenerator.primitive import ProceduralPrimitive -from lfr.netlistgenerator.v2.connectingoption import ConnectingOption class MUX(ProceduralPrimitive): @@ -18,7 +18,8 @@ def generate_input_connectingoptions(self, subgraph_view) -> List[ConnectingOpti be connected to the primitive Args: - subgraph_view (networkx.Graph.subgraph): A subgraph view of the Fluid Interaction Graph + subgraph_view (networkx.Graph.subgraph): A subgraph view of the Fluid + Interaction Graph Raises: NotImplementedError: Raised when its not implemented diff --git a/lfr/netlistgenerator/v2/procedural_component_algorithms/mux3d.py b/lfr/netlistgenerator/procedural_component_algorithms/mux3d.py similarity index 92% rename from lfr/netlistgenerator/v2/procedural_component_algorithms/mux3d.py rename to lfr/netlistgenerator/procedural_component_algorithms/mux3d.py index d9662cd..9f3d9c0 100644 --- a/lfr/netlistgenerator/v2/procedural_component_algorithms/mux3d.py +++ b/lfr/netlistgenerator/procedural_component_algorithms/mux3d.py @@ -1,7 +1,7 @@ +from lfr.netlistgenerator.connectingoption import ConnectingOption from typing import List from lfr.netlistgenerator.primitive import ProceduralPrimitive -from lfr.netlistgenerator.v2.connectingoption import ConnectingOption class MUX3D(ProceduralPrimitive): @@ -18,7 +18,8 @@ def generate_input_connectingoptions(self, subgraph_view) -> List[ConnectingOpti be connected to the primitive Args: - subgraph_view (networkx.Graph.subgraph): A subgraph view of the Fluid Interaction Graph + subgraph_view (networkx.Graph.subgraph): A subgraph view of the Fluid + Interaction Graph Raises: NotImplementedError: Raised when its not implemented @@ -53,7 +54,8 @@ def generate_carrier_connectingoptions( be connected to the primitive Args: - subgraph_view (networkx.Graph.subgraph): A subgraph view of the Fluid Interaction Graph + subgraph_view (networkx.Graph.subgraph): A subgraph view of the Fluid + Interaction Graph Raises: NotImplementedError: Raised when its not implemented @@ -70,7 +72,8 @@ def generate_loading_connectingoptions( be connected to the primitive Args: - subgraph_view (networkx.Graph.subgraph): A subgraph view of the Fluid Interaction Graph + subgraph_view (networkx.Graph.subgraph): A subgraph view of the Fluid + Interaction Graph Raises: NotImplementedError: Raised when its not implemented diff --git a/lfr/netlistgenerator/v2/procedural_component_algorithms/transposer.py b/lfr/netlistgenerator/procedural_component_algorithms/transposer.py similarity index 97% rename from lfr/netlistgenerator/v2/procedural_component_algorithms/transposer.py rename to lfr/netlistgenerator/procedural_component_algorithms/transposer.py index a3200df..8080d48 100644 --- a/lfr/netlistgenerator/v2/procedural_component_algorithms/transposer.py +++ b/lfr/netlistgenerator/procedural_component_algorithms/transposer.py @@ -1,7 +1,7 @@ +from lfr.netlistgenerator.connectingoption import ConnectingOption from typing import List from lfr.netlistgenerator.primitive import ProceduralPrimitive -from lfr.netlistgenerator.v2.connectingoption import ConnectingOption class TRANSPOSER(ProceduralPrimitive): diff --git a/lfr/netlistgenerator/v2/procedural_component_algorithms/tree.py b/lfr/netlistgenerator/procedural_component_algorithms/tree.py similarity index 97% rename from lfr/netlistgenerator/v2/procedural_component_algorithms/tree.py rename to lfr/netlistgenerator/procedural_component_algorithms/tree.py index 7569f96..ec898a1 100644 --- a/lfr/netlistgenerator/v2/procedural_component_algorithms/tree.py +++ b/lfr/netlistgenerator/procedural_component_algorithms/tree.py @@ -1,7 +1,7 @@ +from lfr.netlistgenerator.connectingoption import ConnectingOption from typing import List from lfr.netlistgenerator.primitive import ProceduralPrimitive -from lfr.netlistgenerator.v2.connectingoption import ConnectingOption class TREE(ProceduralPrimitive): diff --git a/lfr/netlistgenerator/v2/procedural_component_algorithms/ytree.py b/lfr/netlistgenerator/procedural_component_algorithms/ytree.py similarity index 98% rename from lfr/netlistgenerator/v2/procedural_component_algorithms/ytree.py rename to lfr/netlistgenerator/procedural_component_algorithms/ytree.py index 0b28874..d31698c 100644 --- a/lfr/netlistgenerator/v2/procedural_component_algorithms/ytree.py +++ b/lfr/netlistgenerator/procedural_component_algorithms/ytree.py @@ -1,10 +1,10 @@ +from lfr.netlistgenerator.connectingoption import ConnectingOption from typing import List, Optional from pymint import MINTComponent, MINTLayer from lfr.netlistgenerator.namegenerator import NameGenerator from lfr.netlistgenerator.primitive import ProceduralPrimitive -from lfr.netlistgenerator.v2.connectingoption import ConnectingOption class YTREE(ProceduralPrimitive): diff --git a/lfr/netlistgenerator/v2/__init__.py b/lfr/netlistgenerator/v2/__init__.py deleted file mode 100644 index e69de29..0000000 From 54aeb17ef8c51ce5353f4e5e538bd33e05777c1f Mon Sep 17 00:00:00 2001 From: Radhakrishna Sanka Date: Wed, 23 Jun 2021 16:54:59 -0400 Subject: [PATCH 18/36] Updating the code to reduce problems from pylance --- lfr/compiler/language/fluidexpression.py | 24 +- lfr/graphmatch/figmappingmatcher.py | 4 +- lfr/graphmatch/interface.py | 10 +- lfr/lfrbaseListener.py | 10 +- lfr/netlistgenerator/constructiongraph.py | 109 +-- lfr/netlistgenerator/generator.py | 40 +- lfr/netlistgenerator/namegenerator.py | 1 - lfr/netlistgenerator/netlistsizor.py | 3 +- .../packaged_libraries/dropx.py | 672 +++++++++--------- lfr/netlistgenerator/primitive.py | 139 ++-- 10 files changed, 547 insertions(+), 465 deletions(-) diff --git a/lfr/compiler/language/fluidexpression.py b/lfr/compiler/language/fluidexpression.py index d4f7433..5ea7e8e 100644 --- a/lfr/compiler/language/fluidexpression.py +++ b/lfr/compiler/language/fluidexpression.py @@ -22,12 +22,13 @@ def process_expression( self, termlist: List[Union[VectorRange, float]], operatorlist: List[str] ): - # In step1, we go over and complete all the numeric operations in the precedence of - # numeric operation order. It is possible that there are no numeric operations going - # on in the expression. In that case we have the option to go to the next step. It - # is also possible there are purely numeric operations and hence we only delete terms - # if the numeric operation happens. In the second scenario, logic dictates that every - # find will require us to delete the operator and the term. + # In step1, we go over and complete all the numeric operations in the + # precedence of numeric operation order. It is possible that there are no + # numeric operations going on in the expression. In that case we have the + # option to go to the next step. It is also possible there are purely numeric + # operations and hence we only delete terms if the numeric operation happens. + # In the second scenario, logic dictates that every find will require us to + # delete the operator and the term. # First go over the numeric operator order for operatorset in self.numeric_operator_order: @@ -182,19 +183,22 @@ def __evalute_fluid_fluid_operator( "Unsuppored operator on two fluid values: {0}".format(operator) ) elif operator == "+": - # TODO: We need to return the operation node here that is generated by operating on the + # TODO: We need to return the operation node here that is generated by + # operating on the # two different operators interactiontype = InteractionType.MIX elif operator == "-": - # TODO: In case of substraction, we need to return the operand1 back again, - # since the subtracted node becomes an output, of whats given to the fluid + # TODO: In case of substraction, we need to return the operand1 back + # again, since the subtracted node becomes an output, of whats given + # to the fluid interactiontype = InteractionType.SIEVE else: raise Exception( "Unsuppored operator on two fluid values: {0}".format(operator) ) - # Check if the operation here is between two different fluids or a fluid and an fluidinteraction + # Check if the operation here is between two different fluids or a fluid + # and an fluidinteraction if isinstance(operand1_element, Interaction) and isinstance( operand2_element, Interaction ): diff --git a/lfr/graphmatch/figmappingmatcher.py b/lfr/graphmatch/figmappingmatcher.py index 8974a4c..9ea7bdb 100644 --- a/lfr/graphmatch/figmappingmatcher.py +++ b/lfr/graphmatch/figmappingmatcher.py @@ -56,7 +56,9 @@ def semantic_feasibility(self, G1_node, G2_node): should consider multigraphs. """ - # Get the semantic information from here and then use it to figure out if its right type of node or not + # Get the semantic information from here and then use it to figure out if + # its right type of node or not + # Figure out if G1 or G2 is the pattern graph g1_fig_info = self._fig.get_fignode(G1_node) g2_semantic_info = self._semantic_information[G2_node] diff --git a/lfr/graphmatch/interface.py b/lfr/graphmatch/interface.py index dd29031..07adc4d 100644 --- a/lfr/graphmatch/interface.py +++ b/lfr/graphmatch/interface.py @@ -47,7 +47,8 @@ def get_fig_matches(fig: FluidInteractionGraph, library: MappingLibrary): ret = [] # TODO - Retrun the networkx subgraph views of the of the FIG - # Step 1 - Generate the match candidates by running the subgraph isomerism for all the items stored in the library + # Step 1 - Generate the match candidates by running the subgraph isomerism for all + # the items stored in the library for (mint, match_pattern_string) in library.get_match_patterns(): if match_pattern_string == "" or match_pattern_string is None: print("Warning ! - Missing match string for mint- {}".format(mint)) @@ -59,11 +60,14 @@ def get_fig_matches(fig: FluidInteractionGraph, library: MappingLibrary): GM = FIGMappingMatcher(fig, structural_template, semantic_information) for subgraph in GM.subgraph_isomorphisms_iter(): - # Work with these subgraphs at the end and then push them through the constraint checking phase + # Work with these subgraphs at the end and then push them through the + # constraint checking phase + # This would be a match, figure out how to get the mapping from GM.mapping # Loop through each of the candates - # TODO - Compare the constraints on all the nodes for this for subgraph to confirm the match + # TODO - Compare the constraints on all the nodes for this for subgraph to + # confirm the match if match_node_constraints( fig, structural_template, semantic_information, subgraph, GM.mapping ): diff --git a/lfr/lfrbaseListener.py b/lfr/lfrbaseListener.py index 78c14de..2d5dcc0 100644 --- a/lfr/lfrbaseListener.py +++ b/lfr/lfrbaseListener.py @@ -382,7 +382,8 @@ def exitExpressionterm(self, ctx: lfrXParser.ExpressiontermContext): self.stack.append(output) elif ctx.number() is not None: - # TODO: Figure out how one needs to process the number with a unary operator + # TODO: Figure out how one needs to process the number with a unary + # operator raise Exception( "Implement method to evaluate number with unary operator" ) @@ -424,6 +425,8 @@ def exitBracketexpression(self, ctx: lfrXParser.BracketexpressionContext): operator = ctx.unary_operator().getText() term = self.stack.pop() + if self.currentModule is None: + raise ValueError() fluidexpession = FluidExpression(self.currentModule) result = fluidexpession.process_unary_operation(term, operator) self.stack.append(result) @@ -468,7 +471,8 @@ def exitAssignstat(self, ctx: lfrXParser.AssignstatContext): def exitLiteralassignstat(self, ctx: lfrXParser.LiteralassignstatContext): rhs = self.stack.pop() lhs = ctx.ID().getText() - # TODO: Check all error conditions and if the right kinds of variables are being assigned here + # TODO: Check all error conditions and if the right kinds of variables are + # being assigned here # self.vectors[lhs] = rhs if self.listermode is ListenerMode.VARIABLE_DECLARATION_MODE: @@ -523,6 +527,8 @@ def __performUnaryOperation( ) ) # TODO: Return the vector range result of unary operator + if self.currentModule is None: + raise ValueError() fluidexpression = FluidExpression(self.currentModule) result = fluidexpression.process_unary_operation(operand, operator) diff --git a/lfr/netlistgenerator/constructiongraph.py b/lfr/netlistgenerator/constructiongraph.py index dbd6489..825eeb2 100644 --- a/lfr/netlistgenerator/constructiongraph.py +++ b/lfr/netlistgenerator/constructiongraph.py @@ -4,7 +4,6 @@ from networkx import nx from networkx.algorithms import isomorphism from networkx.classes.digraph import DiGraph -from networkx.classes.function import subgraph from pymint.mintcomponent import MINTComponent from pymint.mintdevice import MINTDevice from pymint.minttarget import MINTTarget @@ -74,7 +73,8 @@ def construct_components( # Create a new component here based on the primitive technology # and the name generator # Then merge with the larger device - # Save the copy of subgraph view of the netlist in the construction node + # Save the copy of subgraph view of the netlist in the + # construction node component_to_add = None if ( @@ -102,7 +102,8 @@ def construct_components( # Create a new component here based on the primitive technology # and the name generator # Then merge with the larger device - # Save the copy of subgraph view of the netlist in the construction node + # Save the copy of subgraph view of the netlist in the construction + # node component_to_add = mapping_option.primitive.get_default_component( name_generator, layer ) @@ -167,8 +168,10 @@ def split_cn( between all the nodes based on how the FIG subgraph is connected Args: - cn_to_split (ConstructionNode): Construction node that needs to be split into multiple nodes - split_groups (List[List[str]]): A list of lists where each list should contain the FIG node IDs that neet to be in differnt nodes + cn_to_split (ConstructionNode): Construction node that needs to be split + into multiple nodes + split_groups (List[List[str]]): A list of lists where each list should + contain the FIG node IDs that neet to be in differnt nodes """ name = cn_to_split.id fig_nodes = [] @@ -181,7 +184,7 @@ def split_cn( cn = ConstructionNode("{}_split_{}".format(name, group_index)) # Copy all the mapping options but have a differet fig_subgraph for mapping_option in cn_to_split.mapping_options: - # TODO = Figure out what kind of a network mapping I need to get for this + # TODO = Figure out what kind of a network mapping I need to get mapping_copy = copy(mapping_option) mapping_copy.fig_subgraph = fig_subgraph cn.add_mapping_option(mapping_option) @@ -251,30 +254,36 @@ def construct_connections( # src = self._construction_nodes[edge[0]] # tar = self._construction_nodes[edge[1]] - # Step 2 - Get the output requirement from src mapping option and the input mapping - # option and make a simple channel between them (I guess no parameters right now) + # Step 2 - Get the output requirement from src mapping option and the input + # mapping option and make a simple channel between them (I guess no parameters + # right now) # TODO - Modify this based on what mapping option is enabled here later on # src.load_connection_options() # tar.load_connection_options() - # Step 3 - Loop through all the nodes and start filling out this input/output requirements - # This exploration could be using the reverse DFS traversal this way we can probably fill out - # the entire set of flows that way. + # Step 3 - Loop through all the nodes and start filling out this input/output + # requirements + # + # This exploration could be using the reverse DFS traversal this way we can + # probably fill out the entire set of flows that way. # ----------------------- - # TODO - This could probably be converted into network flow problems. Since this an extension - # of bipartitie matching at every step, I think that can be converted into a max flow problem - # However, what needs to be determined is what factor becomes the capacity, weight, etc. - # The only constraints that are known is that everything will have infinite capacity, - # Technically every node might be an edge and every edge might be a node, that way we can - # take the input / output capacities and treat them as. This needs to eb thought through a little - - # We first do the channel creation for the pass throughs so that we don't finish up the input - # output resources. + # TODO - This could probably be converted into network flow problems. Since + # this an extension of bipartitie matching at every step, I think that can be + # converted into a max flow problem However, what needs to be determined is + # what factor becomes the capacity, weight, etc. The only constraints that are + # known is that everything will have infinite capacity, Technically every node + # might be an edge and every edge might be a node, that way we can take the + # input / output capacities and treat them as. This needs to eb thought + # through a little + + # We first do the channel creation for the pass throughs so that we don't + # finish up the input output resources. skip_list = [] for cn_id in self.nodes: - # Step 3.1 - Check to see if there are as many input options are there are incoming edges - # if its n->n or n->1, it'll be easy otherwise we need to figure out something else + # Step 3.1 - Check to see if there are as many input options are there are + # incoming edges if its n->n or n->1, it'll be easy otherwise we need to + # figure out something else in_neighbours = self.in_edges(cn_id) cn = self._construction_nodes[cn_id] @@ -282,11 +291,15 @@ def construct_connections( continue # TODO - Go through netlist and then figure out what needs to get done - # based on the strategy we need to do different things. This is the requirement for when its a + # based on the strategy we need to do different things. This is the + # requirement for when its a # FLOW-FLOW-CONSTRUCTION-NODE - # In this case it needs to treat as an empty netlist because a pass through would just connect the neighbours instead - # TODO - This will potentially get removed later as we might just want to eliminate the construction node later on + # In this case it needs to treat as an empty netlist because a pass through + # would just connect the neighbours instead + # TODO - This will potentially get removed later as we might just want to + # eliminate the construction node later on + # if isinstance(cn.mapping_options[0], NetworkMappingOption): # if cn.mapping_options[0].mapping_type is NetworkMappingOptionType.PASS_THROUGH: # # Figure out what the pass through strategy is for this, find the input @@ -310,7 +323,9 @@ def construct_connections( # device # ) - # TODO - Figure out if these conditions require any more thought in terms of implementation + # TODO - Figure out if these conditions require any more thought in terms + # of implementation + # elif self._mapping_type is NetworkMappingOptionType.COMPONENT_REPLACEMENT: # # TODO - In this case it needs to be an component with the corresponding # # input and output options loaded into the placeholder primitive @@ -326,12 +341,15 @@ def construct_connections( if cn_id in skip_list: continue - # Step 3.1 - Check to see if there are as many input options are there are incoming edges - # if its n->n or n->1, it'll be easy otherwise we need to figure out something else + # Step 3.1 - Check to see if there are as many input options are there are + # incoming edges + # if its n->n or n->1, it'll be easy otherwise we need to figure out + # something else in_neighbours = self.in_edges(cn_id) cn = self._construction_nodes[cn_id] - # Check if any inputs are left to deal with , skip if there are no more inputs left + # Check if any inputs are left to deal with , skip if there are no + # more inputs left if len(cn.input_options) == 0: continue @@ -435,8 +453,8 @@ def __create_passthrough_channel( "0", ) - # TODO - Once we are done creating a path, we need to delete the start and end point options - # from their respective construction nodes. + # TODO - Once we are done creating a path, we need to delete the start and end + # point options from their respective construction nodes. print( "Updated the connectionoptions in {} - Removing {}".format( cn_start, start_point @@ -517,8 +535,8 @@ def __create_intercn_channel( "0", ) - # TODO - Once we are done creating a path, we need to delete the start and end point options - # from their respective construction nodes. + # TODO - Once we are done creating a path, we need to delete the start and end + # point options from their respective construction nodes. print( "Updated the connectionoptions in {} - Removing {}".format(src, start_point) ) @@ -529,18 +547,18 @@ def __create_intercn_channel( def generate_edges(self, fig: FluidInteractionGraph) -> None: # Look at the mapping options for each of the constructionnodes, - # Figure out which other subgraphs are they connected to based on the original fig - # connectivity make the constructionnode based on which other cn subgraphs they are - # connected to + # Figure out which other subgraphs are they connected to based on the original + # fig connectivity make the constructionnode based on which other cn subgraphs + # they are connected to - # Step 1 - create a map for each fig element and see what all cn's they're present in - # (this will account for double coverage cases too) + # Step 1 - create a map for each fig element and see what all cn's they're + # present in (this will account for double coverage cases too) # Step 2 - Now that we know the mapping, go through each connection in the fig, - # Step 3 - if both source and target are in the same cn, skip, create an edge between - # the cn's + # Step 3 - if both source and target are in the same cn, skip, create an edge + # between the cn's - # Step 1 - create a map for each fig element and see what all cn's they're present in - # (this will account for double coverage cases too) + # Step 1 - create a map for each fig element and see what all cn's they're + # present in (this will account for double coverage cases too) # TODO - For combinatorial design space, figure out what to do with this fig_nodes_cn_reverse_map: Dict[str, List[str]] = {} @@ -646,13 +664,16 @@ def generate_edges(self, fig: FluidInteractionGraph) -> None: # ) continue else: - # TODO - When the asserts fail, its an overcoverage issue, decide what needs to be done here + # TODO - When the asserts fail, its an overcoverage issue, decide what + # needs to be done here src_cn = fig_nodes_cn_reverse_map[src] assert len(src_cn) == 1 tar_cn = fig_nodes_cn_reverse_map[tar] assert len(tar_cn) == 1 - # Step 3 - now check to see if both are in the same cn or not, if they're not create an cn_edge + # Step 3 - now check to see if both are in the same cn or not, if + # they're not create an cn_edge + # TODO - implement list search/edge creation incase there are multiple cn's associated if src_cn[0] != tar_cn[0]: self.add_edge(src_cn[0], tar_cn[0]) diff --git a/lfr/netlistgenerator/generator.py b/lfr/netlistgenerator/generator.py index d1d0bc0..6eb91ad 100644 --- a/lfr/netlistgenerator/generator.py +++ b/lfr/netlistgenerator/generator.py @@ -939,8 +939,9 @@ def generate(module: Module, library: MappingLibrary) -> MINTDevice: # if its a 1-n / n-1 / n-n construction nodes, then create a construction node # capturing the whole network - # TODO - Deal with coverage issues here since we need to figure out what are the flow networks, - # that we want to match first and then ensure that they're no included on any list + # TODO - Deal with coverage issues here since we need to figure out what are the + # flow networks, that we want to match first and then ensure that they're no + # included on any list cn_nodes = get_flow_flow_candidates(module, active_strategy) for cn in cn_nodes: construction_graph.add_construction_node(cn) @@ -1000,8 +1001,8 @@ def override_mappings( # mapping options # Step 1 - Loop through each of the mappingtemplates # Step 2 - Loop through each of the instances in teh mappingtemplate - # Step 3 - Find the cn associated with each of the fig nodes and override the - # explicit mapping if mappingtemplate has an associated technology string + # Step 3 - Find the cn associated with each of the fig nodes and override + # the explicit mapping if mappingtemplate has an associated technology string for mapping in mappings: for instance in mapping.instances: @@ -1082,7 +1083,7 @@ def override_network_mappings( # Step 1 - Loop through each of the mappingtemplates # Step 2 - Loop through each of the instances in teh mappingtemplate # Step 3 - Find the cn associated with each of the fig nodes and override the - # explicit mapping if mappingtemplate has an associated technology string + # explicit mapping if mappingtemplate has an associated technology string assign_node_index = 0 for mapping in mappings: for instance in mapping.instances: @@ -1273,6 +1274,15 @@ def connect_orphan_IO(): def get_flow_flow_candidates( module: Module, gen_strategy: GenStrategy ) -> List[ConstructionNode]: + """Get canddiates where it its a "flow" only sub graph + + Args: + module (Module): the module we want to check + gen_strategy (GenStrategy): the generation strategy we want to use + + Returns: + List[ConstructionNode]: List of all the construction nodes + """ # TODO - go through all the edges and see which ones are between flow-flow graphs # If these connectsions are between flow-flow nodes then we need to figure out # which ones are part of the same network/connected graphs with only flow nodes @@ -1340,21 +1350,15 @@ def get_flow_flow_candidates( return ret -# def size_netlist(): -# # Size all the node's netlist components to based on the CONSTRAINTS set -# # by the postprocessor -# # TODO - Modify datastructure in library and other places -# netlist_user_constriants = module.get_user_constriants() - -# construction_graph.fix_component_params(netlist_user_constriants) - -# # Size all the Meter/Dilute/Divide nodes based on the value nodes -# # TODO - Talk to Ali about this for strategy -# construction_graph.size_components() +def __check_if_passthrough(sub) -> bool: + """Checks if its a passthrough chain + Args: + sub (subgraph): subgraph -def __check_if_passthrough(sub) -> bool: - # Return true if its a single chain of flow channels + Returns: + bool: Return true if its a single chain of flow channels + """ in_count = 0 out_count = 0 for node in list(sub.nodes): diff --git a/lfr/netlistgenerator/namegenerator.py b/lfr/netlistgenerator/namegenerator.py index 8a8b201..82abe8e 100644 --- a/lfr/netlistgenerator/namegenerator.py +++ b/lfr/netlistgenerator/namegenerator.py @@ -1,5 +1,4 @@ from typing import Dict - from pymint import MINTComponent, MINTConnection, MINTDevice diff --git a/lfr/netlistgenerator/netlistsizor.py b/lfr/netlistgenerator/netlistsizor.py index 42b1903..fb795bc 100644 --- a/lfr/netlistgenerator/netlistsizor.py +++ b/lfr/netlistgenerator/netlistsizor.py @@ -11,8 +11,7 @@ def __init__(self, netlist_generator): self.blacklist_map = netlist_generator.blacklist_map def size_netlist(self): - from .dafdadapter import ( - ConstraintList, + from lfr.netlistgenerator.dafdadapter import ( DAFDSizingAdapter, FunctionalConstraint, GeometryConstraint, diff --git a/lfr/netlistgenerator/packaged_libraries/dropx.py b/lfr/netlistgenerator/packaged_libraries/dropx.py index 2a014d8..30368b1 100644 --- a/lfr/netlistgenerator/packaged_libraries/dropx.py +++ b/lfr/netlistgenerator/packaged_libraries/dropx.py @@ -8,399 +8,399 @@ def generate_dropx_library() -> MappingLibrary: library = MappingLibrary("dropX") - # PORT - port_inputs = [] - port_inputs.append(ConnectingOption(None, [None])) - port_inputs.append(ConnectingOption(None, [None])) - port_inputs.append(ConnectingOption(None, [None])) - port_inputs.append(ConnectingOption(None, [None])) - port_inputs.append(ConnectingOption(None, [None])) - port_inputs.append(ConnectingOption(None, [None])) - port_inputs.append(ConnectingOption(None, [None])) - port_inputs.append(ConnectingOption(None, [None])) - port_inputs.append(ConnectingOption(None, [None])) - port_inputs.append(ConnectingOption(None, [None])) - port_inputs.append(ConnectingOption(None, [None])) - port_inputs.append(ConnectingOption(None, [None])) - port_inputs.append(ConnectingOption(None, [None])) - port_inputs.append(ConnectingOption(None, [None])) - port_inputs.append(ConnectingOption(None, [None])) - port_inputs.append(ConnectingOption(None, [None])) - port_inputs.append(ConnectingOption(None, [None])) - port_inputs.append(ConnectingOption(None, [None])) - port_inputs.append(ConnectingOption(None, [None])) - - port_outputs = [] - port_outputs.append(ConnectingOption(None, [])) - port_outputs.append(ConnectingOption(None, [])) - port_outputs.append(ConnectingOption(None, [])) - port_outputs.append(ConnectingOption(None, [])) - port_outputs.append(ConnectingOption(None, [])) - port_outputs.append(ConnectingOption(None, [])) - port_outputs.append(ConnectingOption(None, [])) - port_outputs.append(ConnectingOption(None, [])) - port_outputs.append(ConnectingOption(None, [])) - port_outputs.append(ConnectingOption(None, [])) - port_outputs.append(ConnectingOption(None, [])) - port_outputs.append(ConnectingOption(None, [])) - port_outputs.append(ConnectingOption(None, [])) - port_outputs.append(ConnectingOption(None, [])) - port_outputs.append(ConnectingOption(None, [])) - port_outputs.append(ConnectingOption(None, [])) - port_outputs.append(ConnectingOption(None, [])) - port_outputs.append(ConnectingOption(None, [])) - port_outputs.append(ConnectingOption(None, [])) - - port = Primitive( - "PORT", - PrimitiveType.COMPONENT, - "IO", - False, - False, - port_inputs, - port_outputs, - None, - None, - None, - None, - None, - ) - - library.add_io_entry(port) - - # PICO INJECTOR - - pico_injector_inputs = [] - - pico_injector_inputs.append(ConnectingOption(None, [1])) - pico_injector_inputs.append(ConnectingOption(None, [2])) - - pico_injector_outputs = [] - - pico_injector_outputs.append(ConnectingOption(None, [3])) - - pico_injector_loadings = [] - pico_injector_carriers = [] - - pico_injector = Primitive( - "PICO INJECTOR", - PrimitiveType.COMPONENT, - "MIX", - False, - False, - pico_injector_inputs, - pico_injector_outputs, - pico_injector_loadings, - pico_injector_carriers, - None, - ) - - library.add_operator_entry(pico_injector, InteractionType.MIX) - - # DROPLET ELECTROPHORESIS MERGER - - electrophoresis_merger_inputs = [] - - electrophoresis_merger_inputs.append(ConnectingOption(None, [1])) - electrophoresis_merger_inputs.append(ConnectingOption(None, [2])) - - electrophoresis_merger_outputs = [] - - electrophoresis_merger_outputs.append(ConnectingOption(None, [3])) - - electrophoresis_merger_loadings = [] - electrophoresis_merger_carriers = [] - - electrophoresis_merger = Primitive( - "DROPLET ELECTROPHORESIS MERGER", - PrimitiveType.COMPONENT, - "MIX", - False, - False, - electrophoresis_merger_inputs, - electrophoresis_merger_outputs, - electrophoresis_merger_loadings, - electrophoresis_merger_carriers, - None, - ) - - library.add_operator_entry(electrophoresis_merger, InteractionType.MIX) - - # DROPLET GENERATOR - - droplet_generator_inputs = [] - - droplet_generator_inputs.append(ConnectingOption("default_component", [1])) - - droplet_generator_outputs = [] - - droplet_generator_outputs.append(ConnectingOption("default_component", [3])) - - droplet_generator_loadings = [] - droplet_generator_carriers = [] - - droplet_generator = Primitive( - "NOZZLE DROPLET GENERATOR", - PrimitiveType.NETLIST, - "METER", - False, - False, - droplet_generator_inputs, - droplet_generator_outputs, - droplet_generator_loadings, - droplet_generator_carriers, - "default-netlists/dropletgenerator.mint", - ["droplet_size", "generation_rate"], - [ - "orifice_size", - "aspect_ratio", - "capillary_number", - "expansion_ratio", - "flow_rate_ratio", - "normalized_oil_inlet", - "normalized_orifice_length", - "normalized_water_inlet", - ], - ) + # # PORT + # port_inputs = [] + # port_inputs.append(ConnectingOption(None, [None])) + # port_inputs.append(ConnectingOption(None, [None])) + # port_inputs.append(ConnectingOption(None, [None])) + # port_inputs.append(ConnectingOption(None, [None])) + # port_inputs.append(ConnectingOption(None, [None])) + # port_inputs.append(ConnectingOption(None, [None])) + # port_inputs.append(ConnectingOption(None, [None])) + # port_inputs.append(ConnectingOption(None, [None])) + # port_inputs.append(ConnectingOption(None, [None])) + # port_inputs.append(ConnectingOption(None, [None])) + # port_inputs.append(ConnectingOption(None, [None])) + # port_inputs.append(ConnectingOption(None, [None])) + # port_inputs.append(ConnectingOption(None, [None])) + # port_inputs.append(ConnectingOption(None, [None])) + # port_inputs.append(ConnectingOption(None, [None])) + # port_inputs.append(ConnectingOption(None, [None])) + # port_inputs.append(ConnectingOption(None, [None])) + # port_inputs.append(ConnectingOption(None, [None])) + # port_inputs.append(ConnectingOption(None, [None])) + + # port_outputs = [] + # port_outputs.append(ConnectingOption(None, [])) + # port_outputs.append(ConnectingOption(None, [])) + # port_outputs.append(ConnectingOption(None, [])) + # port_outputs.append(ConnectingOption(None, [])) + # port_outputs.append(ConnectingOption(None, [])) + # port_outputs.append(ConnectingOption(None, [])) + # port_outputs.append(ConnectingOption(None, [])) + # port_outputs.append(ConnectingOption(None, [])) + # port_outputs.append(ConnectingOption(None, [])) + # port_outputs.append(ConnectingOption(None, [])) + # port_outputs.append(ConnectingOption(None, [])) + # port_outputs.append(ConnectingOption(None, [])) + # port_outputs.append(ConnectingOption(None, [])) + # port_outputs.append(ConnectingOption(None, [])) + # port_outputs.append(ConnectingOption(None, [])) + # port_outputs.append(ConnectingOption(None, [])) + # port_outputs.append(ConnectingOption(None, [])) + # port_outputs.append(ConnectingOption(None, [])) + # port_outputs.append(ConnectingOption(None, [])) + + # port = Primitive( + # "PORT", + # PrimitiveType.COMPONENT, + # "IO", + # False, + # False, + # port_inputs, + # port_outputs, + # None, + # None, + # None, + # None, + # None, + # ) + + # library.add_io_entry(port) + + # # PICO INJECTOR + + # pico_injector_inputs = [] + + # pico_injector_inputs.append(ConnectingOption(None, [1])) + # pico_injector_inputs.append(ConnectingOption(None, [2])) + + # pico_injector_outputs = [] + + # pico_injector_outputs.append(ConnectingOption(None, [3])) + + # pico_injector_loadings = [] + # pico_injector_carriers = [] + + # pico_injector = Primitive( + # "PICO INJECTOR", + # PrimitiveType.COMPONENT, + # "MIX", + # False, + # False, + # pico_injector_inputs, + # pico_injector_outputs, + # pico_injector_loadings, + # pico_injector_carriers, + # None, + # ) + + # library.add_operator_entry(pico_injector, InteractionType.MIX) + + # # DROPLET ELECTROPHORESIS MERGER + + # electrophoresis_merger_inputs = [] + + # electrophoresis_merger_inputs.append(ConnectingOption(None, [1])) + # electrophoresis_merger_inputs.append(ConnectingOption(None, [2])) + + # electrophoresis_merger_outputs = [] + + # electrophoresis_merger_outputs.append(ConnectingOption(None, [3])) + + # electrophoresis_merger_loadings = [] + # electrophoresis_merger_carriers = [] + + # electrophoresis_merger = Primitive( + # "DROPLET ELECTROPHORESIS MERGER", + # PrimitiveType.COMPONENT, + # "MIX", + # False, + # False, + # electrophoresis_merger_inputs, + # electrophoresis_merger_outputs, + # electrophoresis_merger_loadings, + # electrophoresis_merger_carriers, + # None, + # ) + + # library.add_operator_entry(electrophoresis_merger, InteractionType.MIX) + + # # DROPLET GENERATOR + + # droplet_generator_inputs = [] + + # droplet_generator_inputs.append(ConnectingOption("default_component", [1])) + + # droplet_generator_outputs = [] + + # droplet_generator_outputs.append(ConnectingOption("default_component", [3])) + + # droplet_generator_loadings = [] + # droplet_generator_carriers = [] + + # droplet_generator = Primitive( + # "NOZZLE DROPLET GENERATOR", + # PrimitiveType.NETLIST, + # "METER", + # False, + # False, + # droplet_generator_inputs, + # droplet_generator_outputs, + # droplet_generator_loadings, + # droplet_generator_carriers, + # "default-netlists/dropletgenerator.mint", + # ["droplet_size", "generation_rate"], + # [ + # "orifice_size", + # "aspect_ratio", + # "capillary_number", + # "expansion_ratio", + # "flow_rate_ratio", + # "normalized_oil_inlet", + # "normalized_orifice_length", + # "normalized_water_inlet", + # ], + # ) - library.add_operator_entry(droplet_generator, InteractionType.METER) + # library.add_operator_entry(droplet_generator, InteractionType.METER) - droplet_merger_junction_inputs = [] + # droplet_merger_junction_inputs = [] - droplet_merger_junction_inputs.append(ConnectingOption(None, [1])) - droplet_merger_junction_inputs.append(ConnectingOption(None, [2])) + # droplet_merger_junction_inputs.append(ConnectingOption(None, [1])) + # droplet_merger_junction_inputs.append(ConnectingOption(None, [2])) - droplet_merger_junction_outputs = [] + # droplet_merger_junction_outputs = [] - droplet_merger_junction_outputs.append(ConnectingOption(None, [3])) + # droplet_merger_junction_outputs.append(ConnectingOption(None, [3])) - droplet_merger_junction_loadings = [] - droplet_merger_junction_carriers = [] + # droplet_merger_junction_loadings = [] + # droplet_merger_junction_carriers = [] - droplet_merger_junction = Primitive( - "DROPLET MERGER JUNCTION", - PrimitiveType.COMPONENT, - "MIX", - False, - False, - droplet_merger_junction_inputs, - droplet_merger_junction_outputs, - droplet_merger_junction_loadings, - droplet_merger_junction_carriers, - None, - ) + # droplet_merger_junction = Primitive( + # "DROPLET MERGER JUNCTION", + # PrimitiveType.COMPONENT, + # "MIX", + # False, + # False, + # droplet_merger_junction_inputs, + # droplet_merger_junction_outputs, + # droplet_merger_junction_loadings, + # droplet_merger_junction_carriers, + # None, + # ) - library.add_operator_entry(droplet_merger_junction, InteractionType.MIX) + # library.add_operator_entry(droplet_merger_junction, InteractionType.MIX) - # DROPLET MERGER CHANNEL + # # DROPLET MERGER CHANNEL - droplet_merger_channel_inputs = [] + # droplet_merger_channel_inputs = [] - droplet_merger_channel_inputs.append(ConnectingOption(None, [1])) + # droplet_merger_channel_inputs.append(ConnectingOption(None, [1])) - droplet_merger_channel_outputs = [] + # droplet_merger_channel_outputs = [] - droplet_merger_channel_outputs.append(ConnectingOption(None, [2])) + # droplet_merger_channel_outputs.append(ConnectingOption(None, [2])) - droplet_merger_channel_loadings = [] - droplet_merger_channel_carriers = [] + # droplet_merger_channel_loadings = [] + # droplet_merger_channel_carriers = [] - droplet_merger_channel = Primitive( - "DROPLET MERGER CHANNEL", - PrimitiveType.COMPONENT, - "MIX", - False, - False, - droplet_merger_channel_inputs, - droplet_merger_channel_outputs, - droplet_merger_channel_loadings, - droplet_merger_channel_carriers, - None, - ) + # droplet_merger_channel = Primitive( + # "DROPLET MERGER CHANNEL", + # PrimitiveType.COMPONENT, + # "MIX", + # False, + # False, + # droplet_merger_channel_inputs, + # droplet_merger_channel_outputs, + # droplet_merger_channel_loadings, + # droplet_merger_channel_carriers, + # None, + # ) - library.add_operator_entry(droplet_merger_channel, InteractionType.MIX) + # library.add_operator_entry(droplet_merger_channel, InteractionType.MIX) - # MIXER - CONTINOUS FLOW ONE + # # MIXER - CONTINOUS FLOW ONE - cf_mixer_inputs = [] + # cf_mixer_inputs = [] - cf_mixer_inputs.append(ConnectingOption(None, [1])) - cf_mixer_inputs.append(ConnectingOption(None, [1])) - cf_mixer_inputs.append(ConnectingOption(None, [1])) - cf_mixer_inputs.append(ConnectingOption(None, [1])) - cf_mixer_inputs.append(ConnectingOption(None, [1])) - cf_mixer_inputs.append(ConnectingOption(None, [1])) - cf_mixer_inputs.append(ConnectingOption(None, [1])) - cf_mixer_inputs.append(ConnectingOption(None, [1])) - cf_mixer_inputs.append(ConnectingOption(None, [1])) - cf_mixer_inputs.append(ConnectingOption(None, [1])) - - cf_mixer_outputs = [] - - cf_mixer_outputs.append(ConnectingOption(None, [2])) - - cf_mixer_loadings = [] - cf_mixer_carriers = [] - - cf_mixer = Primitive( - "MIXER", - PrimitiveType.COMPONENT, - "MIX", - False, - False, - cf_mixer_inputs, - cf_mixer_outputs, - cf_mixer_loadings, - cf_mixer_carriers, - None, - ) + # cf_mixer_inputs.append(ConnectingOption(None, [1])) + # cf_mixer_inputs.append(ConnectingOption(None, [1])) + # cf_mixer_inputs.append(ConnectingOption(None, [1])) + # cf_mixer_inputs.append(ConnectingOption(None, [1])) + # cf_mixer_inputs.append(ConnectingOption(None, [1])) + # cf_mixer_inputs.append(ConnectingOption(None, [1])) + # cf_mixer_inputs.append(ConnectingOption(None, [1])) + # cf_mixer_inputs.append(ConnectingOption(None, [1])) + # cf_mixer_inputs.append(ConnectingOption(None, [1])) + # cf_mixer_inputs.append(ConnectingOption(None, [1])) + + # cf_mixer_outputs = [] + + # cf_mixer_outputs.append(ConnectingOption(None, [2])) + + # cf_mixer_loadings = [] + # cf_mixer_carriers = [] + + # cf_mixer = Primitive( + # "MIXER", + # PrimitiveType.COMPONENT, + # "MIX", + # False, + # False, + # cf_mixer_inputs, + # cf_mixer_outputs, + # cf_mixer_loadings, + # cf_mixer_carriers, + # None, + # ) - library.add_operator_entry(cf_mixer, InteractionType.MIX) + # library.add_operator_entry(cf_mixer, InteractionType.MIX) - # DROPLET SPLITTER + # # DROPLET SPLITTER - droplet_splitter_inputs = [] + # droplet_splitter_inputs = [] - droplet_splitter_inputs.append(ConnectingOption(None, [1])) + # droplet_splitter_inputs.append(ConnectingOption(None, [1])) - droplet_splitter_outputs = [] + # droplet_splitter_outputs = [] - droplet_splitter_outputs.append(ConnectingOption(None, [2])) - droplet_splitter_outputs.append(ConnectingOption(None, [3])) + # droplet_splitter_outputs.append(ConnectingOption(None, [2])) + # droplet_splitter_outputs.append(ConnectingOption(None, [3])) - droplet_splitter_loadings = [] - droplet_splitter_carriers = [] + # droplet_splitter_loadings = [] + # droplet_splitter_carriers = [] - droplet_splitter = Primitive( - "DROPLET SPLITTER", - PrimitiveType.COMPONENT, - "DIVIDE", - False, - False, - droplet_splitter_inputs, - droplet_splitter_outputs, - droplet_splitter_loadings, - droplet_splitter_carriers, - None, - ) + # droplet_splitter = Primitive( + # "DROPLET SPLITTER", + # PrimitiveType.COMPONENT, + # "DIVIDE", + # False, + # False, + # droplet_splitter_inputs, + # droplet_splitter_outputs, + # droplet_splitter_loadings, + # droplet_splitter_carriers, + # None, + # ) - library.add_operator_entry(droplet_splitter, InteractionType.DIVIDE) + # library.add_operator_entry(droplet_splitter, InteractionType.DIVIDE) - # DROPLET CAPACITANCE SENSOR + # # DROPLET CAPACITANCE SENSOR - droplet_capacitance_sensor_inputs = [] + # droplet_capacitance_sensor_inputs = [] - droplet_capacitance_sensor_inputs.append(ConnectingOption(None, [1])) + # droplet_capacitance_sensor_inputs.append(ConnectingOption(None, [1])) - droplet_capacitance_sensor_outputs = [] + # droplet_capacitance_sensor_outputs = [] - droplet_capacitance_sensor_outputs.append(ConnectingOption(None, [2])) + # droplet_capacitance_sensor_outputs.append(ConnectingOption(None, [2])) - droplet_capacitance_sensor_loadings = [] - droplet_capacitance_sensor_carriers = [] + # droplet_capacitance_sensor_loadings = [] + # droplet_capacitance_sensor_carriers = [] - droplet_capacitance_sensor = Primitive( - "DROPLET CAPACITANCE SENSOR", - PrimitiveType.COMPONENT, - "PROCESS", - False, - False, - droplet_capacitance_sensor_inputs, - droplet_capacitance_sensor_outputs, - droplet_capacitance_sensor_loadings, - droplet_capacitance_sensor_carriers, - None, - ) + # droplet_capacitance_sensor = Primitive( + # "DROPLET CAPACITANCE SENSOR", + # PrimitiveType.COMPONENT, + # "PROCESS", + # False, + # False, + # droplet_capacitance_sensor_inputs, + # droplet_capacitance_sensor_outputs, + # droplet_capacitance_sensor_loadings, + # droplet_capacitance_sensor_carriers, + # None, + # ) - library.add_operator_entry( - droplet_capacitance_sensor, InteractionType.TECHNOLOGY_PROCESS - ) + # library.add_operator_entry( + # droplet_capacitance_sensor, InteractionType.TECHNOLOGY_PROCESS + # ) - # DROPLET FLUORESCENCE SENSOR + # # DROPLET FLUORESCENCE SENSOR - droplet_fluorescence_sensor_inputs = [] + # droplet_fluorescence_sensor_inputs = [] - droplet_fluorescence_sensor_inputs.append(ConnectingOption(None, [1])) + # droplet_fluorescence_sensor_inputs.append(ConnectingOption(None, [1])) - droplet_fluorescence_sensor_outputs = [] + # droplet_fluorescence_sensor_outputs = [] - droplet_fluorescence_sensor_outputs.append(ConnectingOption(None, [2])) + # droplet_fluorescence_sensor_outputs.append(ConnectingOption(None, [2])) - droplet_fluorescence_sensor_loadings = [] - droplet_fluorescence_sensor_carriers = [] + # droplet_fluorescence_sensor_loadings = [] + # droplet_fluorescence_sensor_carriers = [] - droplet_fluorescence_sensor = Primitive( - "DROPLET FLUORESCENCE SENSOR", - PrimitiveType.COMPONENT, - "PROCESS", - False, - False, - droplet_fluorescence_sensor_inputs, - droplet_fluorescence_sensor_outputs, - droplet_fluorescence_sensor_loadings, - droplet_fluorescence_sensor_carriers, - None, - ) + # droplet_fluorescence_sensor = Primitive( + # "DROPLET FLUORESCENCE SENSOR", + # PrimitiveType.COMPONENT, + # "PROCESS", + # False, + # False, + # droplet_fluorescence_sensor_inputs, + # droplet_fluorescence_sensor_outputs, + # droplet_fluorescence_sensor_loadings, + # droplet_fluorescence_sensor_carriers, + # None, + # ) - library.add_operator_entry( - droplet_fluorescence_sensor, InteractionType.TECHNOLOGY_PROCESS - ) + # library.add_operator_entry( + # droplet_fluorescence_sensor, InteractionType.TECHNOLOGY_PROCESS + # ) - # DROPLET LUMINESCENCE SENSOR - droplet_luminescence_sensor_inputs = [] + # # DROPLET LUMINESCENCE SENSOR + # droplet_luminescence_sensor_inputs = [] - droplet_luminescence_sensor_inputs.append(ConnectingOption(None, [1])) + # droplet_luminescence_sensor_inputs.append(ConnectingOption(None, [1])) - droplet_luminescence_sensor_outputs = [] + # droplet_luminescence_sensor_outputs = [] - droplet_luminescence_sensor_outputs.append(ConnectingOption(None, [2])) + # droplet_luminescence_sensor_outputs.append(ConnectingOption(None, [2])) - droplet_luminescence_sensor_loadings = [] - droplet_luminescence_sensor_carriers = [] + # droplet_luminescence_sensor_loadings = [] + # droplet_luminescence_sensor_carriers = [] - droplet_luminescence_sensor = Primitive( - "DROPLET CAPACITANCE SENSOR", - PrimitiveType.COMPONENT, - "PROCESS", - False, - False, - droplet_luminescence_sensor_inputs, - droplet_luminescence_sensor_outputs, - droplet_luminescence_sensor_loadings, - droplet_luminescence_sensor_carriers, - None, - ) + # droplet_luminescence_sensor = Primitive( + # "DROPLET CAPACITANCE SENSOR", + # PrimitiveType.COMPONENT, + # "PROCESS", + # False, + # False, + # droplet_luminescence_sensor_inputs, + # droplet_luminescence_sensor_outputs, + # droplet_luminescence_sensor_loadings, + # droplet_luminescence_sensor_carriers, + # None, + # ) - library.add_operator_entry( - droplet_luminescence_sensor, InteractionType.TECHNOLOGY_PROCESS - ) + # library.add_operator_entry( + # droplet_luminescence_sensor, InteractionType.TECHNOLOGY_PROCESS + # ) - # DROPLET SPACER + # # DROPLET SPACER - droplet_spacer_inputs = [] + # droplet_spacer_inputs = [] - droplet_spacer_inputs.append(ConnectingOption("default_component", [1])) + # droplet_spacer_inputs.append(ConnectingOption("default_component", [1])) - droplet_spacer_outputs = [] + # droplet_spacer_outputs = [] - droplet_spacer_outputs.append(ConnectingOption("default_component", [2])) + # droplet_spacer_outputs.append(ConnectingOption("default_component", [2])) - droplet_spacer_loadings = [] - droplet_spacer_carriers = [] + # droplet_spacer_loadings = [] + # droplet_spacer_carriers = [] - droplet_spacer = Primitive( - "DROPLET SPACER", - PrimitiveType.NETLIST, - "PROCESS", - False, - False, - droplet_spacer_inputs, - droplet_spacer_outputs, - droplet_spacer_loadings, - droplet_spacer_carriers, - "default-netlists/dropletspacer.mint", - ) - - library.add_operator_entry(droplet_spacer, InteractionType.TECHNOLOGY_PROCESS) + # droplet_spacer = Primitive( + # "DROPLET SPACER", + # PrimitiveType.NETLIST, + # "PROCESS", + # False, + # False, + # droplet_spacer_inputs, + # droplet_spacer_outputs, + # droplet_spacer_loadings, + # droplet_spacer_carriers, + # "default-netlists/dropletspacer.mint", + # ) + + # library.add_operator_entry(droplet_spacer, InteractionType.TECHNOLOGY_PROCESS) return library diff --git a/lfr/netlistgenerator/primitive.py b/lfr/netlistgenerator/primitive.py index 7162d27..ccbbb58 100644 --- a/lfr/netlistgenerator/primitive.py +++ b/lfr/netlistgenerator/primitive.py @@ -114,6 +114,22 @@ def output_params(self): def get_default_component( self, name_gen: NameGenerator, layer: MINTLayer ) -> MINTComponent: + """Gets the default component for the primitive + + Utilizes the NameGenerator instance to generate a new component instance of + the corresponding MINT type + + Args: + name_gen (NameGenerator): NameGenerator instance that will generate the + new name for the component + layer (MINTLayer): Layer object in which the component exists + + Raises: + Exception: Raises an exception when the entry is not of the type COMPONENT + + Returns: + MINTComponent: New component object + """ if self.type is not PrimitiveType.COMPONENT: raise Exception("Cannot execute this method for this kind of a primitive") name = name_gen.generate_name(self.mint) @@ -125,16 +141,20 @@ def get_default_netlist(self, cn_id: str, name_gen: NameGenerator) -> MINTDevice Args: cn_id (str): ID of the construction node so that we can prefix the id's of - all the components that are part of the default netlist + all the components that are part of the default netlist name_gen (NameGenerator): A namegenerator instance that is used for the - globally for synthesizing the design + globally for synthesizing the design Returns: MINTDevice: Default netlist of whatever the primitive is """ if self.type is not PrimitiveType.NETLIST: raise Exception("Cannot execute this method for this kind of a primitive") - + if self._default_netlist is None: + raise Exception( + "Cannot parse MINT file for primitive {} since default netlist" + " parameter is set to None".format(self.mint) + ) default_mint_file = parameters.LIB_DIR.joinpath(self._default_netlist).resolve() device = MINTDevice.from_mint_file(str(default_mint_file)) @@ -206,17 +226,21 @@ def get_procedural_component( def generate_input_connectingoptions(self, subgraph_view) -> List[ConnectingOption]: """Generates a list of connection options that represent where the inputs can - be connected to the primitive + be connected to the primitive - Args: - subgraph_view (networkx.Graph.subgraph): A subgraph view of the Fluid - Interaction Graph + Args: + subgraph_view (networkx.Graph.subgraph): A subgraph view of the Fluid + <<<<<<< HEAD + Interaction Graph + ======= + Interaction Graph + >>>>>>> Updating the code to reduce problems from pylance - Raises: - NotImplementedError: Raised when its not implemented + Raises: + NotImplementedError: Raised when its not implemented - Returns: - List[ConnectingOption]: List of options where we can attach connections + Returns: + List[ConnectingOption]: List of options where we can attach connections """ raise NotImplementedError() @@ -224,18 +248,22 @@ def generate_output_connectingoptions( self, subgraph_view ) -> List[ConnectingOption]: """Generates a list of connection options that represent where the outputs can - be connected to the primitive + be connected to the primitive - Args: - subgraph_view (networkx.Graph.subgraph): A subgraph view of the Fluid - Interaction Graph + Args: + subgraph_view (networkx.Graph.subgraph): A subgraph view of the Fluid + <<<<<<< HEAD + Interaction Graph + ======= + Interaction Graph + >>>>>>> Updating the code to reduce problems from pylance - Raises: - NotImplementedError: Raised when its not implemented + Raises: + NotImplementedError: Raised when its not implemented - Returns: - List[ConnectingOption]: List of options where we can attach connections + Returns: + List[ConnectingOption]: List of options where we can attach connections """ raise NotImplementedError() @@ -243,17 +271,21 @@ def generate_carrier_connectingoptions( self, subgraph_view ) -> List[ConnectingOption]: """Generates a list of connection options that represent where the carrier inputs can - be connected to the primitive + be connected to the primitive - Args: - subgraph_view (networkx.Graph.subgraph): A subgraph view of the Fluid - Interaction Graph + Args: + subgraph_view (networkx.Graph.subgraph): A subgraph view of the Fluid + <<<<<<< HEAD + Interaction Graph + ======= + Interaction Graph + >>>>>>> Updating the code to reduce problems from pylance - Raises: - NotImplementedError: Raised when its not implemented + Raises: + NotImplementedError: Raised when its not implemented - Returns: - List[ConnectingOption]: List of options where we can attach connections + Returns: + List[ConnectingOption]: List of options where we can attach connections """ raise NotImplementedError() @@ -261,17 +293,21 @@ def generate_loading_connectingoptions( self, subgraph_view ) -> List[ConnectingOption]: """Generates a list of connection options that represent where the loading inputs can - be connected to the primitive + be connected to the primitive - Args: - subgraph_view (networkx.Graph.subgraph): A subgraph view of the Fluid - Interaction Graph + Args: + subgraph_view (networkx.Graph.subgraph): A subgraph view of the Fluid + <<<<<<< HEAD + Interaction Graph + ======= + Interaction Graph + >>>>>>> Updating the code to reduce problems from pylance - Raises: - NotImplementedError: Raised when its not implemented + Raises: + NotImplementedError: Raised when its not implemented - Returns: - List[ConnectingOption]: List of options where we can attach connections + Returns: + List[ConnectingOption]: List of options where we can attach connections """ raise NotImplementedError() @@ -297,8 +333,9 @@ def __init__(self, fig_subgraph_view, gen_strategy: GenStrategy) -> None: self._netlist: Optional[MINTDevice] = None def generate_netlist(self) -> None: - """Generates the netlist for the given network primitive, this method generates the flow - network, input , output, carriers and loadings into the primitve properties + """Generates the netlist for the given network primitive, this method generates + the flow network, input , output, carriers and loadings into the primitve + properties """ self._netlist = self._gen_strategy.generate_flow_network( self._fig_subgraph_view @@ -319,17 +356,23 @@ def generate_netlist(self) -> None: def get_default_netlist(self, cn_id: str, name_gen: NameGenerator) -> MINTDevice: """Returns the default netlist for the primitive - Args: - cn_id (str): ID of the construction node so that we can prefix the id's of - all the components that are part of the default netlist - name_gen (NameGenerator): A namegenerator instance that is used for the - globally for synthesizing the design - - Raises: - Exception: Raised when there is no defualt netlist is generated - - Returns: - MINTDevice: Default netlist of whatever the primitive is + Args: + cn_id (str): ID of the construction node so that we can prefix the id's of + <<<<<<< HEAD + all the components that are part of the default netlist + name_gen (NameGenerator): A namegenerator instance that is used for the + globally for synthesizing the design + ======= + all the components that are part of the default netlist + name_gen (NameGenerator): A namegenerator instance that is used for the + globally for synthesizing the design + >>>>>>> Updating the code to reduce problems from pylance + + Raises: + Exception: Raised when there is no defualt netlist is generated + + Returns: + MINTDevice: Default netlist of whatever the primitive is """ if self._netlist is None: raise Exception("No default netlist present for the primitive") From 1090e563ef2a7cf3c9efa438fc0f3571f472b3ab Mon Sep 17 00:00:00 2001 From: Radhakrishna Sanka Date: Fri, 25 Jun 2021 15:49:40 -0400 Subject: [PATCH 19/36] Just documentation --- lfr/netlistgenerator/constructiongraph.py | 79 ++++++++++++++++------- lfr/netlistgenerator/constructionnode.py | 60 ++++++++++++++++- 2 files changed, 115 insertions(+), 24 deletions(-) diff --git a/lfr/netlistgenerator/constructiongraph.py b/lfr/netlistgenerator/constructiongraph.py index 825eeb2..bfa7c35 100644 --- a/lfr/netlistgenerator/constructiongraph.py +++ b/lfr/netlistgenerator/constructiongraph.py @@ -1,5 +1,5 @@ from copy import copy -from typing import Dict, List, Set, Tuple +from typing import Dict, List, Optional, Set, Tuple from networkx import nx from networkx.algorithms import isomorphism @@ -21,6 +21,12 @@ class ConstructionGraph(nx.DiGraph): + """Construction Graph is the proxy datastructure that we use for representing the + loose connections between the fluid interaction graph and the real hardware + design primitives that would be pieced together. + + """ + def __init__(self, data=None, val=None, **attr) -> None: super(ConstructionGraph, self).__init__() self._construction_nodes: Dict[str, ConstructionNode] = {} @@ -28,8 +34,35 @@ def __init__(self, data=None, val=None, **attr) -> None: self._component_refs: Dict[str, List[str]] = {} self._fixed_edges: List[Tuple[str, str]] = [] + # TODO - Figure out if this is where we want to store this + self._connection_technology: Optional[str] = None + + @property + def connection_technology(self) -> Optional[str]: + """Returns the default connection technology being used during + connection creation + + Returns: + Optional[str]: MINT string + """ + return self._connection_technology + + @connection_technology.setter + def connection_technology(self, technology_string: str) -> None: + """Sets the default connection technology + + Args: + technology_string (str): MINT technology string + """ + self._connection_technology = technology_string + @property def construction_nodes(self) -> List[ConstructionNode]: + """Returns the list of construction nodes in the construction graph + + Returns: + List[ConstructionNode]: + """ return [v for k, v in self._construction_nodes.items()] def get_cn(self, id: str) -> ConstructionNode: @@ -39,8 +72,8 @@ def get_cn(self, id: str) -> ConstructionNode: raise KeyError() def add_construction_node(self, node: ConstructionNode) -> None: - self._construction_nodes[node.id] = node - self.add_node(node.id) + self._construction_nodes[node.ID] = node + self.add_node(node.ID) def delete_node(self, id: str) -> None: self.remove_node(id) @@ -68,7 +101,7 @@ def construct_components( continue elif ( mapping_option.mapping_type - == NetworkMappingOptionType.COMPONENT_REPLACEMENT + is NetworkMappingOptionType.COMPONENT_REPLACEMENT ): # Create a new component here based on the primitive technology # and the name generator @@ -94,7 +127,7 @@ def construct_components( ) device.add_component(component_to_add) - self._component_refs[cn.id] = [component_to_add.ID] + self._component_refs[cn.ID] = [component_to_add.ID] # for connecting_option in cn # TODO - save the subgraph view reference @@ -108,14 +141,14 @@ def construct_components( name_generator, layer ) device.add_component(component_to_add) - self._component_refs[cn.id] = [component_to_add.ID] + self._component_refs[cn.ID] = [component_to_add.ID] # for connecting_option in cn # TODO - save the subgraph view reference elif mapping_option.primitive.type is PrimitiveType.NETLIST: netlist = mapping_option.primitive.get_default_netlist( - cn.id, name_generator + cn.ID, name_generator ) - self._component_refs[cn.id] = [ + self._component_refs[cn.ID] = [ component.ID for component in netlist.components ] device.merge_netlist(netlist) @@ -123,9 +156,9 @@ def construct_components( # TODO - Save the subgraph view reference elif mapping_option.primitive.type is PrimitiveType.PROCEDURAL: netlist = mapping_option.primitive.get_default_netlist( - cn.id, name_generator + cn.ID, name_generator ) - self._component_refs[cn.id] = [ + self._component_refs[cn.ID] = [ component.ID for component in netlist.components ] device.merge_netlist(netlist) @@ -173,7 +206,7 @@ def split_cn( split_groups (List[List[str]]): A list of lists where each list should contain the FIG node IDs that neet to be in differnt nodes """ - name = cn_to_split.id + name = cn_to_split.ID fig_nodes = [] for nodes in split_groups: fig_nodes.extend(nodes) @@ -192,7 +225,7 @@ def split_cn( self.add_construction_node(cn) # Delete the node now - self.delete_node(cn_to_split.id) + self.delete_node(cn_to_split.ID) # TODO - create connections between the cns based on the figs self.generate_edges(full_subgraph) @@ -209,17 +242,17 @@ def insert_cn( # Delete all the edges between the input nodes and the output nodes for input_cn in input_cns: for output_cn in output_cns: - if self.has_edge(input_cn.id, output_cn.id): - self.remove_edge(input_cn.id, output_cn.id) + if self.has_edge(input_cn.ID, output_cn.ID): + self.remove_edge(input_cn.ID, output_cn.ID) # Add the cn self.add_construction_node(cn_to_insert) # Create the connections between the intermediate cn for input_cn in input_cns: - self.add_edge(input_cn.id, cn_to_insert.id) + self.add_edge(input_cn.ID, cn_to_insert.ID) for output_cn in output_cns: - self.add_edge(cn_to_insert.id, output_cn.id) + self.add_edge(cn_to_insert.ID, output_cn.ID) def get_component_cn(self, component: MINTComponent) -> ConstructionNode: """Get the Construction Node associated with the given component @@ -426,7 +459,9 @@ def __create_passthrough_channel( ) # TODO - Change how we retrieve the technology type for the channel - tech_string = "CHANNEL" + if self._connection_technology is None: + raise ValueError() + tech_string = self._connection_technology # channel_name = name_generator.generate_name(tech_string) # TODO - Figure out how to hande a scenario where this isn't ture @@ -492,10 +527,10 @@ def __create_intercn_channel( if end_point.component_name is None: # This means a single component was mapped here - tar_component_name = self._component_refs[cn.id][0] + tar_component_name = self._component_refs[cn.ID][0] else: tar_component_name = name_generator.get_cn_name( - cn.id, end_point.component_name + cn.ID, end_point.component_name ) print( @@ -587,11 +622,11 @@ def generate_edges(self, fig: FluidInteractionGraph) -> None: if node_id in fig_nodes_cn_reverse_map.keys(): # Make sure there are no repeats here - if cn.id not in fig_nodes_cn_reverse_map[node_id]: - fig_nodes_cn_reverse_map[node_id].append(cn.id) + if cn.ID not in fig_nodes_cn_reverse_map[node_id]: + fig_nodes_cn_reverse_map[node_id].append(cn.ID) else: fig_nodes_cn_reverse_map[node_id] = [] - fig_nodes_cn_reverse_map[node_id].append(cn.id) + fig_nodes_cn_reverse_map[node_id].append(cn.ID) # Step 1.5 - Handle the overcoverage scenarios, currently we assume that # the undercoverage scenarios are only in the case of networkmappings. diff --git a/lfr/netlistgenerator/constructionnode.py b/lfr/netlistgenerator/constructionnode.py index 4811306..5cb5117 100644 --- a/lfr/netlistgenerator/constructionnode.py +++ b/lfr/netlistgenerator/constructionnode.py @@ -43,25 +43,57 @@ def output_options(self) -> List[ConnectingOption]: @property def loading_options(self) -> List[ConnectingOption]: + """Returns the list of loading options for the mapption option candidate + + Returns: + List[ConnectingOption]: List of loading options + """ return self._loading_options @property def carrier_options(self) -> List[ConnectingOption]: + """Returns the list of carrier options set for the construction node + + Returns: + List[ConnectingOption]: List of carrier options + """ return self._carrier_options @property - def id(self) -> str: + def ID(self) -> str: + """Returns the id of the construction node + + Returns: + str: ID of the construction node + """ return self._id @property def mapping_options(self) -> List[MappingOption]: + """Returns the list connecting options on the + construction node + + Returns: + List[MappingOption]: Mapping options available for the construction node + """ return self._mapping_options @mapping_options.setter def mapping_options(self, options: List[MappingOption]): + """Sets the mapping options + + Args: + options (List[MappingOption]): MappingOptions + """ self._mapping_options = options def use_explicit_mapping(self, mapping: MappingOption) -> None: + """Uses the explicit mapping option passed as the parameter + + Args: + mapping (MappingOption): MappingOption that needs to set + here explicitly + """ # Set the flag for explicit mapping self._explict_mapping_flag = True # Delete all the existing mapping options @@ -82,7 +114,15 @@ def add_mapping_option(self, mapping_option: MappingOption) -> None: self._mapping_options.append(mapping_option) def load_connection_options(self) -> None: + """Loads the corresponding different connecting options into + the construction node + """ # TODO - Figure out what do do if its a combinatorial design + if len(self._mapping_options) != 1: + raise Exception( + "More than one mapping options present for construction node, cannot" + " load connecting options" + ) assert len(self._mapping_options) == 1 primitive_ref = self._mapping_options[0].primitive @@ -105,5 +145,21 @@ def load_connection_options(self) -> None: self._carrier_options = options + def merge_construction_node(self, construction_node: "ConstructionNode") -> None: + """Merges the construction node passed as an arugment into the this + construction node. + + This will let us handle the scenario where we might want to merge + construction nodes that have undercoverage and help support the future + combinatorial generation options + + + Args: + construction_node (ConstructionNode): [description] + """ + raise NotImplementedError( + "Implement this when we are trying to make combinatorial operations work" + ) + def __str__(self) -> str: - return "Construction Node: {}".format(self.id) + return "Construction Node: {}".format(self.ID) From 10de0966a3131766d224ec9ef85d083b694118b7 Mon Sep 17 00:00:00 2001 From: Radhakrishna Sanka Date: Fri, 25 Jun 2021 18:30:35 -0400 Subject: [PATCH 20/36] Renamed construction node property to ID (from id) since id is used to find the pointer of the python object --- generator-old.py | 2 +- lfr/netlistgenerator/gen_strategies/genstrategy.py | 4 ++-- lfr/netlistgenerator/generator.py | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/generator-old.py b/generator-old.py index e230e29..1358ac4 100644 --- a/generator-old.py +++ b/generator-old.py @@ -826,7 +826,7 @@ def eliminate_passthrough_nodes(construction_graph: ConstructionGraph): if isinstance(mapping_option, NetworkMappingOption): if mapping_option.mapping_type is NetworkMappingOptionType.PASS_THROUGH: - print("Eliminating PASS THROUGH construction node = {}".format(cn.id)) + print("Eliminating PASS THROUGH construction node = {}".format(cn.ID)) # First get all the in and out edges in_edges = list(construction_graph.in_edges(node_id)) diff --git a/lfr/netlistgenerator/gen_strategies/genstrategy.py b/lfr/netlistgenerator/gen_strategies/genstrategy.py index 3c72032..75440dd 100644 --- a/lfr/netlistgenerator/gen_strategies/genstrategy.py +++ b/lfr/netlistgenerator/gen_strategies/genstrategy.py @@ -33,7 +33,7 @@ def reduce_mapping_options(self) -> None: # Remove the extra mappings print( "Reducing mapping options for Construction node: {} from {} to {}" - .format(cn.id, len(cn.mapping_options), 1), + .format(cn.ID, len(cn.mapping_options), 1), ) if len(cn.mapping_options) > 1: for option in cn.mapping_options: @@ -43,7 +43,7 @@ def reduce_mapping_options(self) -> None: print("Printing all final mapping options:") for cn in self._construction_graph.construction_nodes: - print("Construction node: {}".format(cn.id)) + print("Construction node: {}".format(cn.ID)) print("Options: ") for mapping_option in cn.mapping_options: diff --git a/lfr/netlistgenerator/generator.py b/lfr/netlistgenerator/generator.py index 6eb91ad..653bf8d 100644 --- a/lfr/netlistgenerator/generator.py +++ b/lfr/netlistgenerator/generator.py @@ -1214,7 +1214,7 @@ def eliminate_passthrough_nodes(construction_graph: ConstructionGraph): if isinstance(mapping_option, NetworkMappingOption): if mapping_option.mapping_type is NetworkMappingOptionType.PASS_THROUGH: - print("Eliminating PASS THROUGH construction node = {}".format(cn.id)) + print("Eliminating PASS THROUGH construction node = {}".format(cn.ID)) # First get all the in and out edges in_edges = list(construction_graph.in_edges(node_id)) From dacde5e2430f98eb57bbb2acc82eda9bf6ff2831 Mon Sep 17 00:00:00 2001 From: Radhakrishna Sanka Date: Mon, 12 Jul 2021 15:58:55 -0400 Subject: [PATCH 21/36] Add a bunch more check for none and modified ID --- generator-old.py | 28 ++++---- lfr/cmdline.py | 6 +- lfr/compiler/distribute/distributeblock.py | 15 +++- lfr/compiler/language/vector.py | 1 - lfr/compiler/language/vectorrange.py | 7 +- lfr/compiler/module.py | 6 +- lfr/distBlockListener.py | 43 ++++++++++-- lfr/fig/fignode.py | 10 +-- lfr/fig/fluidinteractiongraph.py | 68 +++++++++---------- lfr/fig/interaction.py | 21 +++--- lfr/lfrbaseListener.py | 14 ++-- lfr/moduleinstanceListener.py | 2 + lfr/netlistgenerator/constructiongraph.py | 4 +- .../gen_strategies/dropxstrategy.py | 6 +- lfr/netlistgenerator/generator.py | 38 +++++------ 15 files changed, 163 insertions(+), 106 deletions(-) diff --git a/generator-old.py b/generator-old.py index 1358ac4..6dfaa5a 100644 --- a/generator-old.py +++ b/generator-old.py @@ -483,7 +483,7 @@ def generate(module: Module, library: MappingLibrary) -> MINTDevice: # as there would be alternatives for each type of mapping for interaction in module.FIG.get_interactions(): operator_candidates = library.get_operators(interaction_type=interaction.type) - cn = ConstructionNode(interaction.id) + cn = ConstructionNode(interaction.ID) # if isinstance(interaction, ValueNode): # continue @@ -494,14 +494,14 @@ def generate(module: Module, library: MappingLibrary) -> MINTDevice: ): # Basically add the value node id into the subgraph view also node_ids = [ - module.FIG.get_fignode(edge[0]).id - for edge in module.FIG.in_edges(interaction.id) + module.FIG.get_fignode(edge[0]).ID + for edge in module.FIG.in_edges(interaction.ID) if isinstance(module.FIG.get_fignode(edge[0]), ValueNode) ] - node_ids.append(interaction.id) + node_ids.append(interaction.ID) sub_graph = module.FIG.subgraph(node_ids) else: - sub_graph = module.FIG.subgraph(interaction.id) + sub_graph = module.FIG.subgraph(interaction.ID) mapping_option = MappingOption(operator_candidate, sub_graph) cn.add_mapping_option(mapping_option) @@ -650,8 +650,8 @@ def override_mappings( # cn_mapping_options.extend(cn.mapping_options) print( "Skipping Network Mapping: \n Input - {} \n Output - {}".format( - ",".join([n.id for n in instance.input_nodes]), - ",".join([n.id for n in instance.output_nodes]), + ",".join([n.ID for n in instance.input_nodes]), + ",".join([n.ID for n in instance.output_nodes]), ) ) continue @@ -671,12 +671,12 @@ def override_mappings( # In the case of an Fluid Value interaction put all valuenodes in the subgraph node_ids.extend( [ - fig.get_fignode(edge[0]).id - for edge in fig.in_edges(instance.node.id) + fig.get_fignode(edge[0]).ID + for edge in fig.in_edges(instance.node.ID) if isinstance(fig.get_fignode(edge[0]), ValueNode) ] ) - node_ids.append(instance.node.id) + node_ids.append(instance.node.ID) subgraph = fig.subgraph(node_ids) # Get the Construction node that has the corresponding subgraph, @@ -729,13 +729,13 @@ def override_network_mappings( if isinstance(instance, NetworkMapping): print( "Applying Network Mapping: \n Input - {} \n Output - {}".format( - ",".join([n.id for n in instance.input_nodes]), - ",".join([n.id for n in instance.output_nodes]), + ",".join([n.ID for n in instance.input_nodes]), + ",".join([n.ID for n in instance.output_nodes]), ) ) - node_ids.extend(n.id for n in instance.input_nodes) - node_ids.extend(n.id for n in instance.output_nodes) + node_ids.extend(n.ID for n in instance.input_nodes) + node_ids.extend(n.ID for n in instance.output_nodes) subgraph = fig.subgraph(node_ids) try: # TODO - Incase this is a flow-flow candidate, we need to get the diff --git a/lfr/cmdline.py b/lfr/cmdline.py index 135e79d..1fe7e1a 100644 --- a/lfr/cmdline.py +++ b/lfr/cmdline.py @@ -122,9 +122,9 @@ def main(): mapping_listener.print_variables() - interactiongraph = mapping_listener.currentModule.FIG - - printgraph(interactiongraph, mapping_listener.currentModule.name + ".dot") + if mapping_listener.currentModule is not None: + interactiongraph = mapping_listener.currentModule.FIG + printgraph(interactiongraph, mapping_listener.currentModule.name + ".dot") if args.no_gen is True: sys.exit(0) diff --git a/lfr/compiler/distribute/distributeblock.py b/lfr/compiler/distribute/distributeblock.py index b62637e..a740e27 100644 --- a/lfr/compiler/distribute/distributeblock.py +++ b/lfr/compiler/distribute/distributeblock.py @@ -14,7 +14,9 @@ def __init__(self) -> None: self._state_table: Optional[StateTable] = None def generate_fig(self, fig: FluidInteractionGraph) -> None: - # TODO - Create the fig based on the given distribute logic shown here + # Create the fig based on the given distribute logic shown here + if self._state_table is None: + raise ValueError("State Table has not being initialized") print("Implement the fig generation from this") self._state_table.generate_connectivity_table() @@ -59,6 +61,9 @@ def sensitivity_list(self, signal_list: List[VectorRange]) -> None: def set_connectivity(self, state, source, target) -> None: # Make the connectivity here based on the state # This will be called mulitple times per distributeassignstat + if self._state_table is None: + raise ValueError("State Table has not being initialized") + self._state_table.save_connectivity(state, source, target) @staticmethod @@ -79,13 +84,17 @@ def generate_state_vector( self, signal_list: List[VectorRange], value_list: List[bool] ) -> BitVector: # self._state_table.convert_to_fullstate_vector() + + if self._state_table is None: + raise ValueError("State Table has not being initialized") + individual_signal_list = [] individual_value_list = [] i = 0 for signal in signal_list: for j in range(len(signal)): - individual_signal_list.append(signal[j].id) + individual_signal_list.append(signal[j].ID) value = value_list[i + j] individual_value_list.append(value) i += 1 @@ -100,5 +109,5 @@ def __generate_state_header(signal_list: List[VectorRange]) -> List[str]: state_header = [] for vector_range in signal_list: for i in range(len(vector_range)): - state_header.append(vector_range[i].id) + state_header.append(vector_range[i].ID) return state_header diff --git a/lfr/compiler/language/vector.py b/lfr/compiler/language/vector.py index d32ba48..c4d7870 100644 --- a/lfr/compiler/language/vector.py +++ b/lfr/compiler/language/vector.py @@ -1,5 +1,4 @@ from typing import List - from lfr.compiler.language.vectorrange import VectorRange diff --git a/lfr/compiler/language/vectorrange.py b/lfr/compiler/language/vectorrange.py index 85fe2a3..c6b05f7 100644 --- a/lfr/compiler/language/vectorrange.py +++ b/lfr/compiler/language/vectorrange.py @@ -1,9 +1,14 @@ from networkx.algorithms.operators.unary import reverse +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from lfr.compiler.language.vector import Vector + class VectorRange: def __init__(self, vector, startindex: int, endindex: int): - self.vector = vector + self.vector: Vector = vector if startindex is None: self.startindex = 0 else: diff --git a/lfr/compiler/module.py b/lfr/compiler/module.py index 38299ba..ab2e334 100644 --- a/lfr/compiler/module.py +++ b/lfr/compiler/module.py @@ -65,7 +65,7 @@ def get_all_io(self) -> List[ModuleIO]: return self._io def add_fluid(self, fluid: Flow): - self.fluids[fluid.id] = fluid + self.fluids[fluid.ID] = fluid self.FIG.add_fignode(fluid) def get_fluid(self, name: str) -> Optional[FIGNode]: @@ -205,7 +205,7 @@ def instantiate_module( fignode.type is IOType.FLOW_INPUT or fignode.type is IOType.FLOW_OUTPUT ) # Replace - new_fignode = Flow(fignode.id) + new_fignode = Flow(fignode.ID) fig_copy.switch_fignode(fignode, new_fignode) # Step 4 - Relabel all the nodes with the prefix defined by @@ -267,7 +267,7 @@ def instantiate_module( (FluidicOperatorMapping, StorageMapping, PumpMapping), ): # Swap the basic node from original to the instance - there_node_id = mapping_instance.node.id + there_node_id = mapping_instance.node.ID here_node = self.FIG.get_fignode(rename_map[there_node_id]) mapping_instance.node = here_node elif isinstance(mapping_instance, NetworkMapping): diff --git a/lfr/distBlockListener.py b/lfr/distBlockListener.py index 42f8e51..3902ee9 100644 --- a/lfr/distBlockListener.py +++ b/lfr/distBlockListener.py @@ -12,7 +12,7 @@ def __init__(self) -> None: super().__init__() self._current_dist_block: Optional[DistributeBlock] = None self._current_sensitivity_list = None - self._current_state: BitVector + self._current_state: Optional[BitVector] self._current_connectivities: List[Tuple[str, str]] = [] # This particular variable is only used for # figuring out the else statement @@ -35,12 +35,14 @@ def exitDistributeCondition(self, ctx: lfrXParser.DistributeConditionContext): assert rhs == 0 or rhs == 1 assert relation_operator == "==" + if self._current_dist_block is None: + raise ValueError('"_current_dist_block" is set to None') + state_vector = self._current_dist_block.generate_state_vector([lhs], [rhs == 1]) self._current_state = state_vector def enterDistributionBlock(self, ctx: lfrXParser.DistributionBlockContext): print("Entering the Distribution Block") - # TODO - Instantiate the distribute graph or whatever class that encapsulates this self._current_dist_block = DistributeBlock() def exitSensitivitylist(self, ctx: lfrXParser.SensitivitylistContext): @@ -76,11 +78,19 @@ def exitSensitivitylist(self, ctx: lfrXParser.SensitivitylistContext): vrange = VectorRange(v, start_index, end_index) sentivity_list.append(vrange) + if self._current_dist_block is None: + raise ValueError('"_current_dist_block" is set to None') + self._current_dist_block.sensitivity_list = sentivity_list def exitDistributionBlock(self, ctx: lfrXParser.DistributionBlockContext): print("Exit the Distribution Block") # TODO - Generate the fig from the distribute block + if self._current_dist_block is None: + raise ValueError('"_current_dist_block" is set to None') + + if self.currentModule is None: + raise ValueError("Current module set to none") self._current_dist_block.generate_fig(self.currentModule.FIG) def enterDistributionassignstat( @@ -101,18 +111,18 @@ def exitDistributionassignstat(self, ctx: lfrXParser.DistributionassignstatConte print("LHS, RHS sizes are equal") for source, target in zip(rhs, lhs): print(source, target) - sourceid = source.id - targetid = target.id + sourceid = source.ID + targetid = target.ID self._current_connectivities.append((sourceid, targetid)) elif len(lhs) != len(rhs): print("LHS not equal to RHS") for source in rhs: - sourceid = source.id + sourceid = source.ID for target in lhs: - targetid = target.id + targetid = target.ID self._current_connectivities.append((sourceid, targetid)) def enterIfElseBlock(self, ctx: lfrXParser.IfElseBlockContext): @@ -130,7 +140,12 @@ def enterElseIfBlock(self, ctx: lfrXParser.ElseIfBlockContext): def exitIfBlock(self, ctx: lfrXParser.IfBlockContext): # We need to go through all the current connectivities # and put them into the distribute block + if self._current_state is None: + raise ValueError("No state set for if block") self._accumulated_states.append(self._current_state) + if self._current_dist_block is None: + raise ValueError('"_current_dist_block" is set to None') + dist_block = self._current_dist_block for connectivity in self._current_connectivities: dist_block.set_connectivity( @@ -140,7 +155,13 @@ def exitIfBlock(self, ctx: lfrXParser.IfBlockContext): def exitElseIfBlock(self, ctx: lfrXParser.ElseIfBlockContext): # We need to go through all the current connectivities # and put them into the distribute block + if self._current_state is None: + raise ValueError("No state set for if block") self._accumulated_states.append(self._current_state) + + if self._current_dist_block is None: + raise ValueError('"_current_dist_block" is set to None') + dist_block = self._current_dist_block for connectivity in self._current_connectivities: dist_block.set_connectivity( @@ -151,11 +172,15 @@ def enterElseBlock(self, ctx: lfrXParser.ElseBlockContext): self._current_connectivities = [] def exitElseBlock(self, ctx: lfrXParser.ElseBlockContext): + if self._current_dist_block is None: + raise ValueError('"_current_dist_block" is set to None') + remaining_states = self._current_dist_block.get_remaining_states( self._accumulated_states ) for state in remaining_states: for connectivity in self._current_connectivities: + self._current_dist_block.set_connectivity( state, connectivity[0], connectivity[1] ) @@ -175,8 +200,14 @@ def exitCasestat(self, ctx: lfrXParser.CasestatContext): rhs = self.stack.pop() assert isinstance(rhs, BitVector) lhs = self._current_lhs + + if self._current_dist_block is None: + raise ValueError('"_current_dist_block" is set to None') + dist_block = self._current_dist_block rhs_list = [rhs[i] == 1 for i in range(len(rhs))] + if lhs is None: + raise ValueError("LHS set to none in case stat") state_vector = self._current_dist_block.generate_state_vector([lhs], rhs_list) self._current_state = state_vector diff --git a/lfr/fig/fignode.py b/lfr/fig/fignode.py index 9912107..80e8c58 100644 --- a/lfr/fig/fignode.py +++ b/lfr/fig/fignode.py @@ -22,7 +22,7 @@ def __init__(self, id: str) -> None: self._id: str = id @property - def id(self): + def ID(self): return self._id @property @@ -30,11 +30,11 @@ def match_string(self): return "-" def __str__(self) -> str: - return self.id + return self.ID def __eq__(self, other): if isinstance(other, FIGNode): - return self.id == other.id + return self.ID == other.ID else: return False @@ -71,7 +71,7 @@ def match_string(self): return "FLOW" def __str__(self) -> str: - return "FLOW - {}".format(self.id) + return "FLOW - {}".format(self.ID) class IONode(Flow): @@ -88,7 +88,7 @@ def type(self, iotype: IOType) -> None: self._type = iotype def __str__(self) -> str: - return "Name: {0.id}, Type : {0.type}".format(self) + return "Name: {0.ID}, Type : {0.type}".format(self) @property def match_string(self): diff --git a/lfr/fig/fluidinteractiongraph.py b/lfr/fig/fluidinteractiongraph.py index d2b15ca..156a226 100644 --- a/lfr/fig/fluidinteractiongraph.py +++ b/lfr/fig/fluidinteractiongraph.py @@ -53,9 +53,9 @@ def annotations(self) -> List[DistributeAnnotation]: return self._annotations def add_fignode(self, node: FIGNode) -> None: - self._fignodes[node.id] = node + self._fignodes[node.ID] = node self._annotations_reverse_map[node] = [] - self.add_node(node.id) + self.add_node(node.ID) def get_fignode(self, id: str) -> FIGNode: if id in self._fignodes.keys(): @@ -67,7 +67,7 @@ def get_fignode(self, id: str) -> FIGNode: def load_fignodes(self, fig_nodes: List[FIGNode]) -> None: for node in fig_nodes: - self._fignodes[node.id] = node + self._fignodes[node.ID] = node # Add an entry for the reverse map here to make things simpler self._annotations_reverse_map[node] = [] @@ -83,10 +83,10 @@ def load_annotations(self, annotations: List[DistributeAnnotation]) -> None: self.__add_to_reverse_map(item[1], annotation) def contains_fignode(self, fluid_object: FIGNode) -> bool: - return fluid_object.id in self._fignodes.keys() + return fluid_object.ID in self._fignodes.keys() def switch_fignode(self, old_fignode: FIGNode, new_fignode: FIGNode) -> None: - self._fignodes[old_fignode.id] = new_fignode + self._fignodes[old_fignode.ID] = new_fignode def rename_nodes(self, rename_map: Dict[str, str]) -> None: for node in self.nodes: @@ -104,11 +104,11 @@ def rename_annotations(self, rename_map: Dict[str, str]) -> None: annotation.rename(rename_map[annotation.id]) def add_interaction(self, interaction: Interaction): - if interaction.id not in self._fignodes.keys(): + if interaction.ID not in self._fignodes.keys(): self.add_fignode(interaction) else: raise Exception( - "Interaction already present in the FIG: {0}".format(interaction.id) + "Interaction already present in the FIG: {0}".format(interaction.ID) ) if isinstance(interaction, FluidFluidInteraction): @@ -127,20 +127,20 @@ def add_interaction(self, interaction: Interaction): raise Exception("Invalid Interaction Type found here") def connect_fignodes(self, source: FIGNode, target: FIGNode): - if source.id not in self._fignodes.keys(): + if source.ID not in self._fignodes.keys(): raise Exception( "Unable to add interaction because of missing flow: {0}".format( - source.id + source.ID ) ) - if target.id not in self._fignodes.keys(): + if target.ID not in self._fignodes.keys(): raise Exception( "Unable to add interaction because of missing flow: {0}".format( - target.id + target.ID ) ) - self.add_edge(source.id, target.id) + self.add_edge(source.ID, target.ID) def get_interactions(self) -> List[Interaction]: ret = [] @@ -332,54 +332,54 @@ def __get_val_node_id(self) -> str: def __add_fluid_fluid_interaction(self, interaction: FluidFluidInteraction) -> None: # Check if flow exists - if interaction.fluids[0].id not in self._fignodes.keys(): + if interaction.fluids[0].ID not in self._fignodes.keys(): raise Exception( "Unable to add interaction because of missing flow: {0}".format( - interaction.fluids[0].id + interaction.fluids[0].ID ) ) - if interaction.fluids[1].id not in self._fignodes.keys(): + if interaction.fluids[1].ID not in self._fignodes.keys(): raise Exception( "Unable to add interaction because of missing flow: {0}".format( - interaction.fluids[1].id + interaction.fluids[1].ID ) ) - self.add_node(interaction.id) - self.add_edge(interaction.fluids[0].id, interaction.id) + self.add_node(interaction.ID) + self.add_edge(interaction.fluids[0].ID, interaction.ID) # Figure out how we want to connect FIGNodes if interaction.type is InteractionType.SIEVE: # In the case of a SIEVE interaction, we need to add the second # FIGNode as an output - self.add_edge(interaction.id, interaction.fluids[1].id) + self.add_edge(interaction.ID, interaction.fluids[1].ID) else: - self.add_edge(interaction.fluids[1].id, interaction.id) + self.add_edge(interaction.fluids[1].ID, interaction.ID) # TODO: Need to add an output node def __add_single_fluid_interaction( self, interaction: FluidProcessInteraction ) -> None: - if interaction.fluid.id not in self._fignodes.keys(): + if interaction.fluid.ID not in self._fignodes.keys(): raise Exception( "Unable to add interaction because of missing flow: {0}".format( - interaction.fluid.id + interaction.fluid.ID ) ) - self.add_node(interaction.id) - self.add_edge(interaction.fluid.id, interaction.id) + self.add_node(interaction.ID) + self.add_edge(interaction.fluid.ID, interaction.ID) # TODO: Need to add an output node def __add_fluid_number_interaction( self, interaction: FluidNumberInteraction ) -> None: - if interaction.fluid.id not in self._fignodes.keys(): + if interaction.fluid.ID not in self._fignodes.keys(): raise Exception( "Unable to add interaction because of missing flow: {0}".format( - interaction.fluid.id + interaction.fluid.ID ) ) @@ -387,17 +387,17 @@ def __add_fluid_number_interaction( val_node = ValueNode(self.__get_val_node_id(), interaction.value) self.add_fignode(val_node) - self.add_node(interaction.id) - self.add_edge(interaction.fluid.id, interaction.id) - self.add_edge(val_node.id, interaction.id) + self.add_node(interaction.ID) + self.add_edge(interaction.fluid.ID, interaction.ID) + self.add_edge(val_node.ID, interaction.ID) def __add_fluid_integer_interaction( self, interaction: FluidIntegerInteraction ) -> None: - if interaction.fluid.id not in self._fignodes.keys(): + if interaction.fluid.ID not in self._fignodes.keys(): raise Exception( "Unable to add interaction because of missing flow: {0}".format( - interaction.fluid.id + interaction.fluid.ID ) ) @@ -405,6 +405,6 @@ def __add_fluid_integer_interaction( val_node = ValueNode(self.__get_val_node_id(), interaction.value) self.add_fignode(val_node) - self.add_node(interaction.id) - self.add_edge(interaction.fluid.id, interaction.id) - self.add_edge(val_node.id, interaction.id) + self.add_node(interaction.ID) + self.add_edge(interaction.fluid.ID, interaction.ID) + self.add_edge(val_node.ID, interaction.ID) diff --git a/lfr/fig/interaction.py b/lfr/fig/interaction.py index 26bbb39..5abd74f 100644 --- a/lfr/fig/interaction.py +++ b/lfr/fig/interaction.py @@ -42,13 +42,16 @@ def get_id( ) -> str: id = None + if fluid1 is None: + raise ValueError("id of fluid1 is found to be None") + if fluid2 is not None: - if fluid1.id < fluid2.id: - id = fluid1.id + "_" + operator_string + "_" + fluid2.id + if fluid1.ID < fluid2.ID: + id = fluid1.ID + "_" + operator_string + "_" + fluid2.ID else: - id = fluid2.id + "_" + operator_string + "_" + fluid1.id + id = fluid2.ID + "_" + operator_string + "_" + fluid1.ID else: - id = fluid1.id + "_" + operator_string + id = fluid1.ID + "_" + operator_string id = id + "_" + str(Interaction.INTERACTION_ID) Interaction.INTERACTION_ID += 1 @@ -97,10 +100,12 @@ def __init__( """Creates an interaction between two fluids Args: - fluid1 (FIGNode): [description] - fluid2 (FIGNode): [description] - interaction_type (InteractionType, optional): [description]. Defaults to None. - interaction_data (str, optional): [description]. Defaults to None. + fluid1 (FIGNode): Fluid1 that needs to be included in the interaction + fluid2 (FIGNode): Fluid2 that needs to be included in the interaction + interaction_type (InteractionType, optional): Type of Fluid Interaction. + Defaults to None. + interaction_data (str, optional): Interaction data (typically used for + fluid-number interactions). Defaults to None. """ id = Interaction.get_id( fluid1, fluid2, Interaction.get_operator_str(interaction_type) diff --git a/lfr/lfrbaseListener.py b/lfr/lfrbaseListener.py index 2d5dcc0..5fe829a 100644 --- a/lfr/lfrbaseListener.py +++ b/lfr/lfrbaseListener.py @@ -1,3 +1,4 @@ +from os import error import re from enum import Enum from typing import List, Optional @@ -432,6 +433,9 @@ def exitBracketexpression(self, ctx: lfrXParser.BracketexpressionContext): self.stack.append(result) def exitAssignstat(self, ctx: lfrXParser.AssignstatContext): + if self.currentModule is None: + raise ValueError("current module is set to None") + rhs = self.stack.pop() lhs = self.stack.pop() @@ -454,18 +458,18 @@ def exitAssignstat(self, ctx: lfrXParser.AssignstatContext): # Make 1-1 connections for source, target in zip(rhs, lhs): print(source, target) - sourceid = source.id - targetid = target.id + sourceid = source.ID + targetid = target.ID self.currentModule.add_fluid_connection(sourceid, targetid) elif len(lhs) != len(rhs): print("LHS not equal to RHS") for source in rhs: - sourceid = source.id + sourceid = source.ID for target in lhs: - targetid = target.id + targetid = target.ID self.currentModule.add_fluid_connection(sourceid, targetid) def exitLiteralassignstat(self, ctx: lfrXParser.LiteralassignstatContext): @@ -557,6 +561,8 @@ def __parseBinaryNumber(text: str) -> BitVector: pattern = r"(\d+)'b(\d+)" matches = re.search(pattern, text) # size = int(matches.group(1)) + if matches is None: + raise error("No matches found") bit_pattern = matches.group(2) n = BitVector(bitstring=bit_pattern) return n diff --git a/lfr/moduleinstanceListener.py b/lfr/moduleinstanceListener.py index 123a2e6..bf66554 100644 --- a/lfr/moduleinstanceListener.py +++ b/lfr/moduleinstanceListener.py @@ -52,6 +52,8 @@ def exitOrderedioblock(self, ctx: lfrXParser.OrderedioblockContext): variables.insert(0, self.stack.pop()) # now go through the different connections in the module to import + if self._module_to_import is None: + raise ValueError("No module to import here") module_io = self._module_to_import.io assert len(module_io) == num_variables diff --git a/lfr/netlistgenerator/constructiongraph.py b/lfr/netlistgenerator/constructiongraph.py index bfa7c35..49e462d 100644 --- a/lfr/netlistgenerator/constructiongraph.py +++ b/lfr/netlistgenerator/constructiongraph.py @@ -176,12 +176,12 @@ def construct_components( def get_fignode_cn(self, fig_node: FIGNode) -> ConstructionNode: for cn in self._construction_nodes.values(): for mapping_option in cn.mapping_options: - if fig_node.id in mapping_option.fig_subgraph.nodes: + if fig_node.ID in mapping_option.fig_subgraph.nodes: return cn raise Exception( "Could not find construction node with an active mappingoption that covers" - " FIG node: {}".format(fig_node.id) + " FIG node: {}".format(fig_node.ID) ) def split_cn( diff --git a/lfr/netlistgenerator/gen_strategies/dropxstrategy.py b/lfr/netlistgenerator/gen_strategies/dropxstrategy.py index 9f82561..52b9552 100644 --- a/lfr/netlistgenerator/gen_strategies/dropxstrategy.py +++ b/lfr/netlistgenerator/gen_strategies/dropxstrategy.py @@ -23,7 +23,7 @@ def reduce_mapping_options(self) -> None: fignode = self._fig.get_fignode(fignode_id) # check if construction node - if ConstructionNode(fignode.id).is_explictly_mapped: + if ConstructionNode(fignode.ID).is_explictly_mapped: pass # TODO - Implement Generalized Ali Strategy 1 # Rule 1 - The first level of % should be mapping to a Droplet Generator @@ -84,7 +84,7 @@ def reduce_mapping_options(self) -> None: fignode = self._fig.get_fignode(fignode_id) # check if explicitly mapped - if not ConstructionNode(fignode.id).is_explictly_mapped: + if not ConstructionNode(fignode.ID).is_explictly_mapped: if isinstance(fignode, Interaction): if ( fignode.type is InteractionType.MIX @@ -105,7 +105,7 @@ def reduce_mapping_options(self) -> None: fignode = self._fig.get_fignode(fignode_id) # check if map - if not ConstructionNode(fignode.id).is_explictly_mapped: + if not ConstructionNode(fignode.ID).is_explictly_mapped: if isinstance(fignode, Interaction): # if + if ( diff --git a/lfr/netlistgenerator/generator.py b/lfr/netlistgenerator/generator.py index 653bf8d..5260eff 100644 --- a/lfr/netlistgenerator/generator.py +++ b/lfr/netlistgenerator/generator.py @@ -867,7 +867,7 @@ def generate(module: Module, library: MappingLibrary) -> MINTDevice: # Map the interactions in the fig to individual library options for interaction in module.FIG.get_interactions(): operator_candidates = library.get_operators(interaction_type=interaction.type) - cn = ConstructionNode(interaction.id) + cn = ConstructionNode(interaction.ID) # if isinstance(interaction, ValueNode): # continue @@ -878,14 +878,14 @@ def generate(module: Module, library: MappingLibrary) -> MINTDevice: ): # Basically add the value node id into the subgraph view also node_ids = [ - module.FIG.get_fignode(edge[0]).id - for edge in module.FIG.in_edges(interaction.id) + module.FIG.get_fignode(edge[0]).ID + for edge in module.FIG.in_edges(interaction.ID) if isinstance(module.FIG.get_fignode(edge[0]), ValueNode) ] - node_ids.append(interaction.id) + node_ids.append(interaction.ID) sub_graph = module.FIG.subgraph(node_ids) else: - sub_graph = module.FIG.subgraph(interaction.id) + sub_graph = module.FIG.subgraph(interaction.ID) mapping_option = MappingOption(operator_candidate, sub_graph) cn.add_mapping_option(mapping_option) @@ -913,7 +913,7 @@ def generate(module: Module, library: MappingLibrary) -> MINTDevice: for fig_node_id in list(module.FIG.nodes): fig_node = module.FIG.get_fignode(fig_node_id) if isinstance(fig_node, Pump): - cn = ConstructionNode(fig_node.id) + cn = ConstructionNode(fig_node.ID) sub_graph = module.FIG.subgraph(fig_node_id) mapping_candidates = library.get_pump_entries() for mapping_candidate in mapping_candidates: @@ -921,7 +921,7 @@ def generate(module: Module, library: MappingLibrary) -> MINTDevice: cn.add_mapping_option(mapping_option) elif isinstance(fig_node, Storage): - cn = ConstructionNode(fig_node.id) + cn = ConstructionNode(fig_node.ID) sub_graph = module.FIG.subgraph(fig_node_id) mapping_candidates = library.get_storage_entries() for mapping_candidate in mapping_candidates: @@ -1020,8 +1020,8 @@ def override_mappings( if isinstance(instance, NetworkMapping): print( "Skipping Network Mapping: \n Input - {} \n Output - {}".format( - ",".join([n.id for n in instance.input_nodes]), - ",".join([n.id for n in instance.output_nodes]), + ",".join([n.ID for n in instance.input_nodes]), + ",".join([n.ID for n in instance.output_nodes]), ) ) continue @@ -1042,12 +1042,12 @@ def override_mappings( # subgraph node_ids.extend( [ - fig.get_fignode(edge[0]).id - for edge in fig.in_edges(instance.node.id) + fig.get_fignode(edge[0]).ID + for edge in fig.in_edges(instance.node.ID) if isinstance(fig.get_fignode(edge[0]), ValueNode) ] ) - node_ids.append(instance.node.id) + node_ids.append(instance.node.ID) subgraph = fig.subgraph(node_ids) # Get the Construction node that has the corresponding subgraph, @@ -1110,13 +1110,13 @@ def override_network_mappings( if isinstance(instance, NetworkMapping): print( "Applying Network Mapping: \n Input - {} \n Output - {}".format( - ",".join([n.id for n in instance.input_nodes]), - ",".join([n.id for n in instance.output_nodes]), + ",".join([n.ID for n in instance.input_nodes]), + ",".join([n.ID for n in instance.output_nodes]), ) ) - node_ids.extend(n.id for n in instance.input_nodes) - node_ids.extend(n.id for n in instance.output_nodes) + node_ids.extend(n.ID for n in instance.input_nodes) + node_ids.extend(n.ID for n in instance.output_nodes) subgraph = fig.subgraph(node_ids) # try: # # TODO - Incase this is a flow-flow candidate, we need to get the @@ -1310,10 +1310,10 @@ def get_flow_flow_candidates( for mapping in module.mappings: for instance in mapping.instances: if isinstance(instance, NetworkMapping): - remove_list.extend([n.id for n in instance.input_nodes]) - remove_list.extend([n.id for n in instance.output_nodes]) + remove_list.extend([n.ID for n in instance.input_nodes]) + remove_list.extend([n.ID for n in instance.output_nodes]) else: - remove_list.append(instance.node.id) + remove_list.append(instance.node.ID) for node_id in fig_copy.nodes: node = fig_original.get_fignode(node_id) From 38026e452cd09aa01fd946d925d16b7790ab52f6 Mon Sep 17 00:00:00 2001 From: Radhakrishna Sanka Date: Mon, 12 Jul 2021 16:09:34 -0400 Subject: [PATCH 22/36] Fixed yet another error --- lfr/lfrbaseListener.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lfr/lfrbaseListener.py b/lfr/lfrbaseListener.py index 5fe829a..60033b1 100644 --- a/lfr/lfrbaseListener.py +++ b/lfr/lfrbaseListener.py @@ -95,6 +95,9 @@ def exitModule(self, ctx: lfrXParser.ModuleContext): def enterIoblock(self, ctx: lfrXParser.IoblockContext): # If io block has an explicit declaration set the flag + if self.currentModule is None: + raise ValueError("currentModule set to None") + if ctx.explicitIOBlock() is not None: self.EXPLICIT_MODULE_DECLARATION = True From 33f50a04685eedf21e687a32e9885f2296ad2ba6 Mon Sep 17 00:00:00 2001 From: Radhakrishna Sanka Date: Wed, 14 Jul 2021 14:07:00 -0400 Subject: [PATCH 23/36] Added additional test infrastructure for imported designs --- .vscode/launch.json | 15 +++++++++ lfr/cmdline.py | 12 ++++++- lfr/compiler/module.py | 2 ++ lfr/preprocessor.py | 73 +++++++++++++++++++++++++++++++++--------- scripts/all.sh | 20 ++++++------ 5 files changed, 95 insertions(+), 27 deletions(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index 555dc1a..9a9d34f 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -91,6 +91,21 @@ ], "console": "integratedTerminal" }, + { + "name": "Debug - Customfile, NO GEN (with distribute library)", + "type": "python", + "request": "launch", + "program": "${workspaceFolder}/lfr/cmdline.py", + "args": [ + "--no-gen", + "--outpath", + "./out/", + "--pre-load", + "../LFR-Testcases/distribute-library", + "${file}" + ], + "console": "integratedTerminal" + }, { "name": "Debug - CLI TEST", "type": "python", diff --git a/lfr/cmdline.py b/lfr/cmdline.py index 1fe7e1a..d15608f 100644 --- a/lfr/cmdline.py +++ b/lfr/cmdline.py @@ -70,10 +70,20 @@ def main(): " #CONSTRAIN" ), ) + parser.add_argument( + "--pre-load", + type=str, + action="append", + help=( + "This lets the preprocessor look for the different design libraries that" + " need to be added to the memory (avoid using this outside bulk testing)" + ), + ) args = parser.parse_args() + pre_load_file_list = args.pre_load # Utilize the prepreocessor to generate the input file - preprocessor = PreProcessor(args.input) + preprocessor = PreProcessor(args.input, pre_load_file_list) if preprocessor.check_syntax_errors(): print("Stopping compiler because of syntax errors") diff --git a/lfr/compiler/module.py b/lfr/compiler/module.py index ab2e334..da3eeb1 100644 --- a/lfr/compiler/module.py +++ b/lfr/compiler/module.py @@ -189,6 +189,8 @@ def instantiate_module( module_to_import = module_check # Step 2 - Create a copy of the fig + if module_to_import is None: + raise ReferenceError("module_to_import is set to none") fig_copy = copy.deepcopy(module_to_import.FIG) # Step 3 - Convert all the flow IO nodes where mappings exist diff --git a/lfr/preprocessor.py b/lfr/preprocessor.py index d1baf4b..6e58504 100644 --- a/lfr/preprocessor.py +++ b/lfr/preprocessor.py @@ -1,7 +1,7 @@ import re import sys from pathlib import Path -from typing import List +from typing import Dict, List import networkx as nx from antlr4.CommonTokenStream import CommonTokenStream @@ -14,10 +14,23 @@ class PreProcessor: - def __init__(self, file_list: List[str]) -> None: + def __init__(self, file_list: List[str], lib_dir_list: List[str] = []) -> None: self.resolved_paths = {} self.full_text = {} self.text_dump = None + self._lib_file_list: Dict[str, str] = {} # Stores file path to file + + print("Loading all LFR Files from lib Directories:") + for dir_ref in lib_dir_list: + print("-- Loading form path {}".format(dir_ref)) + path = Path(dir_ref).resolve() + + for file in path.rglob("*.lfr"): + path_object = Path(file) + full_path = path_object.resolve() + print("Storing into library: {}".format(full_path)) + self._lib_file_list[str(path_object.name)] = str(full_path) + for file_path in file_list: extension = Path(file_path).suffix @@ -26,18 +39,7 @@ def __init__(self, file_list: List[str]) -> None: sys.exit() p = Path(file_path).resolve() - print("Input Path: {0}".format(p)) - # Open a file: file - file = open(p, mode="r") - - # read all lines at once - all_of_it = file.read() - - # close the file - file.close() - - self.resolved_paths[p.name] = p - self.full_text[p.name] = all_of_it + self.__store_full_text(p) def check_syntax_errors(self) -> bool: syntax_errors = 0 @@ -63,7 +65,10 @@ def process(self) -> None: for file_handle in self.full_text: dep_graph.add_node(file_handle) - for file_handle in self.full_text: + # We extract his because these are all the files defined by the user + user_derfined_list = str(self.full_text.keys()) + + for file_handle in user_derfined_list: # Find all imports and generate the edges text = self.full_text[file_handle] find_results = re.findall(IMPORT_FILE_PATTERN, text) @@ -71,8 +76,25 @@ def process(self) -> None: new_file_handle = result[1] delete_string = result[0] + # Check if the file handle is found in the dependency graph if new_file_handle not in list(dep_graph.nodes): - raise Exception("Could not find file - {}".format(result[1])) + + # Since its not in the dependency graph we check if + # its in the preloaded library + if new_file_handle not in list(self._lib_file_list.keys()): + + # Since its not in the preloaded library either... + raise Exception("Could not find file - {}".format(result[1])) + else: + + # Pull all the text, add it to the full text store + file_path = self._lib_file_list[new_file_handle] + p = Path(file_path).resolve() + print("Using Library Design at Path: {0}".format(p)) + self.__store_full_text(p) + + # Add the file node to the dependency graph here + dep_graph.add_node(new_file_handle) dep_graph.add_edge(file_handle, new_file_handle) @@ -93,3 +115,22 @@ def process(self) -> None: file = open("pre_processor_dump.lfr", "w") file.write(final_dump) file.close() + + def __store_full_text(self, file_path: Path): + """Stores the full text of the give file into the preprocessor store + + Args: + file_path (Path): Path object of the file + """ + print("Input Path: {0}".format(file_path)) + # Open a file: file + file = open(file_path, mode="r") + + # read all lines at once + all_of_it = file.read() + + # close the file + file.close() + + self.resolved_paths[file_path.name] = file_path + self.full_text[file_path.name] = all_of_it diff --git a/scripts/all.sh b/scripts/all.sh index 1791557..300ee76 100755 --- a/scripts/all.sh +++ b/scripts/all.sh @@ -7,7 +7,7 @@ for f in ~/CIDAR/LFR-Testcases/dropx/*.lfr; do echo "Running File $f"; - lfr-compile $f --no-gen --outpath ~/CIDAR/LFR-Dry-Run/dropx/ + lfr-compile $f --no-gen --outpath ~/CIDAR/LFR-Dry-Run/dropx/ --pre-load ~/CIDAR/LFR-Testcases/distribute-library done @@ -17,7 +17,7 @@ for f in ~/CIDAR/LFR-Testcases/chthesis/*.lfr; do echo "Running File $f"; - lfr-compile $f --no-gen --outpath ~/CIDAR/LFR-Dry-Run/chthesis/ + lfr-compile $f --no-gen --outpath ~/CIDAR/LFR-Dry-Run/chthesis/ --pre-load ~/CIDAR/LFR-Testcases/distribute-library done @@ -28,7 +28,7 @@ for f in ~/CIDAR/LFR-Testcases/COVID/*.lfr; do echo "Running File $f"; - lfr-compile $f --no-gen --outpath ~/CIDAR/LFR-Dry-Run/COVID/ + lfr-compile $f --no-gen --outpath ~/CIDAR/LFR-Dry-Run/COVID/ --pre-load ~/CIDAR/LFR-Testcases/distribute-library done @@ -38,7 +38,7 @@ for f in ~/CIDAR/LFR-Testcases/Expressions/*.lfr; do echo "Running File $f"; - lfr-compile $f --no-gen --outpath ~/CIDAR/LFR-Dry-Run/Expressions/ + lfr-compile $f --no-gen --outpath ~/CIDAR/LFR-Dry-Run/Expressions/ --pre-load ~/CIDAR/LFR-Testcases/distribute-library done echo "Synthesizing ghissues" @@ -47,7 +47,7 @@ for f in ~/CIDAR/LFR-Testcases/ghissues/*.lfr; do echo "Running File $f"; - lfr-compile $f --no-gen --outpath ~/CIDAR/LFR-Dry-Run/ghissues/ + lfr-compile $f --no-gen --outpath ~/CIDAR/LFR-Dry-Run/ghissues/ --pre-load ~/CIDAR/LFR-Testcases/distribute-library done @@ -58,7 +58,7 @@ for f in ~/CIDAR/LFR-Testcases/GraphCoverage/*.lfr; do echo "Running File $f"; - lfr-compile $f --no-gen --outpath ~/CIDAR/LFR-Dry-Run/GraphCoverage/ + lfr-compile $f --no-gen --outpath ~/CIDAR/LFR-Dry-Run/GraphCoverage/ --pre-load ~/CIDAR/LFR-Testcases/distribute-library done @@ -68,7 +68,7 @@ for f in ~/CIDAR/LFR-Testcases/ParserTest/*.lfr; do echo "Running File $f"; - lfr-compile $f --no-gen --outpath ~/CIDAR/LFR-Dry-Run/ParserTest/ + lfr-compile $f --no-gen --outpath ~/CIDAR/LFR-Dry-Run/ParserTest/ --pre-load ~/CIDAR/LFR-Testcases/distribute-library done @@ -79,7 +79,7 @@ for f in ~/CIDAR/LFR-Testcases/Protocols/*.lfr; do echo "Running File $f"; - lfr-compile $f --no-gen --outpath ~/CIDAR/LFR-Dry-Run/Protocols/ + lfr-compile $f --no-gen --outpath ~/CIDAR/LFR-Dry-Run/Protocols/ --pre-load ~/CIDAR/LFR-Testcases/distribute-library done @@ -89,7 +89,7 @@ for f in ~/CIDAR/LFR-Testcases/Ryuichi\'s\ designs/*.lfr; do echo "Running File $f"; - lfr-compile $f --no-gen --outpath ~/CIDAR/LFR-Dry-Run/Ryuichi\'s\ designs/ + lfr-compile $f --no-gen --outpath ~/CIDAR/LFR-Dry-Run/Ryuichi\'s\ designs/ --pre-load ~/CIDAR/LFR-Testcases/distribute-library done @@ -99,6 +99,6 @@ for f in ~/CIDAR/LFR-Testcases/TechnologyMapping/*.lfr; do echo "Running File $f"; - lfr-compile $f --no-gen --outpath ~/CIDAR/LFR-Dry-Run/TechnologyMapping/ + lfr-compile $f --no-gen --outpath ~/CIDAR/LFR-Dry-Run/TechnologyMapping/ --pre-load ~/CIDAR/LFR-Testcases/distribute-library done From 6cc798c1afdd5cead45464343ea71cad59bbe312 Mon Sep 17 00:00:00 2001 From: Radhakrishna Sanka Date: Wed, 14 Jul 2021 14:10:39 -0400 Subject: [PATCH 24/36] Incorrect listtification problem --- lfr/preprocessor.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lfr/preprocessor.py b/lfr/preprocessor.py index 6e58504..00a20b6 100644 --- a/lfr/preprocessor.py +++ b/lfr/preprocessor.py @@ -66,7 +66,7 @@ def process(self) -> None: dep_graph.add_node(file_handle) # We extract his because these are all the files defined by the user - user_derfined_list = str(self.full_text.keys()) + user_derfined_list = list(self.full_text.keys()) for file_handle in user_derfined_list: # Find all imports and generate the edges @@ -86,7 +86,7 @@ def process(self) -> None: # Since its not in the preloaded library either... raise Exception("Could not find file - {}".format(result[1])) else: - + # Pull all the text, add it to the full text store file_path = self._lib_file_list[new_file_handle] p = Path(file_path).resolve() From 929956a88a29417399c25280e5a705a24d8f8ba1 Mon Sep 17 00:00:00 2001 From: Radhakrishna Sanka Date: Wed, 14 Jul 2021 16:34:01 -0400 Subject: [PATCH 25/36] Fixed: 1. Incorrect refernce to `id` since refactor did not catch it. 2. Fixed issue where tuples were getting unrolled when appending to the a list for distribute annotation 3. Added String formatting for new fignode types --- lfr/compiler/distribute/statetable.py | 10 +++++++--- lfr/fig/fignode.py | 11 ++++++++++- lfr/moduleinstanceListener.py | 2 +- 3 files changed, 18 insertions(+), 5 deletions(-) diff --git a/lfr/compiler/distribute/statetable.py b/lfr/compiler/distribute/statetable.py index 931fd34..6a15468 100644 --- a/lfr/compiler/distribute/statetable.py +++ b/lfr/compiler/distribute/statetable.py @@ -285,7 +285,6 @@ def generate_or_annotations(self, fig: FluidInteractionGraph) -> None: # Add the connection target in inot the annotion targets we want # this representated for the entire converage target = edge[1] - args_for_annotation.append(fig.get_fignode(target)) # Check if the source is in any of the AND annotations source = edge[0] found_flag = False @@ -300,8 +299,13 @@ def generate_or_annotations(self, fig: FluidInteractionGraph) -> None: args_for_annotation.append(annotation_to_use) else: source_fignode = fig.get_fignode(source) - if source_fignode not in args_for_annotation: - args_for_annotation.append(source_fignode) + target_fignode = fig.get_fignode(target) + if ( + source_fignode in args_for_annotation + and target_fignode in args_for_annotation + ) is False: + item_to_add = (source_fignode, target_fignode) + args_for_annotation.append(item_to_add) self._or_annotations.append(fig.add_or_annotation(args_for_annotation)) diff --git a/lfr/fig/fignode.py b/lfr/fig/fignode.py index 80e8c58..f281621 100644 --- a/lfr/fig/fignode.py +++ b/lfr/fig/fignode.py @@ -88,7 +88,7 @@ def type(self, iotype: IOType) -> None: self._type = iotype def __str__(self) -> str: - return "Name: {0.ID}, Type : {0.type}".format(self) + return "IO - Name: {0.ID}, Type : {0.type}".format(self) @property def match_string(self): @@ -103,6 +103,9 @@ def __init__(self, id: str) -> None: def match_string(self): return "STORAGE" + def __str__(self) -> str: + return "STORAGE - {}".format(self.ID) + class Pump(Flow): def __init__(self, id: str) -> None: @@ -112,6 +115,9 @@ def __init__(self, id: str) -> None: def match_string(self) -> str: return "PUMP" + def __str__(self) -> str: + return "PUMP - {}".format(self.ID) + class Signal(FIGNode): def __init__(self, id: str) -> None: @@ -120,3 +126,6 @@ def __init__(self, id: str) -> None: @property def match_string(self): return "SIGNAL" + + def __str__(self) -> str: + return "SIGNAL - {}".format(self.ID) diff --git a/lfr/moduleinstanceListener.py b/lfr/moduleinstanceListener.py index bf66554..7fbf5b9 100644 --- a/lfr/moduleinstanceListener.py +++ b/lfr/moduleinstanceListener.py @@ -65,7 +65,7 @@ def exitOrderedioblock(self, ctx: lfrXParser.OrderedioblockContext): there_vector_ref = module_io[i].vector_ref here_vector_ref = variables[i] for i in range(len(there_vector_ref)): - self._io_mapping[there_vector_ref[i].id] = here_vector_ref[i].id + self._io_mapping[there_vector_ref[i].ID] = here_vector_ref[i].ID # def exitUnorderedioblock(self, ctx: lfrXParser.UnorderedioblockContext): # num_variables = len(ctx.explicitinstanceiomapping()) From 6eda7b159a7a58d700c5d0f45efd0952d7b1ca20 Mon Sep 17 00:00:00 2001 From: Radhakrishna Sanka Date: Thu, 15 Jul 2021 19:01:42 -0400 Subject: [PATCH 26/36] Modified a couple of error scenarios --- lfr/compiler/module.py | 31 ++++++++++++++++++++----------- lfr/fig/fluidinteractiongraph.py | 24 ++++++++++++------------ 2 files changed, 32 insertions(+), 23 deletions(-) diff --git a/lfr/compiler/module.py b/lfr/compiler/module.py index da3eeb1..adeaee1 100644 --- a/lfr/compiler/module.py +++ b/lfr/compiler/module.py @@ -52,6 +52,12 @@ def get_explicit_mappings(self) -> List[NodeMappingTemplate]: def add_io(self, io: ModuleIO): self._io.append(io) for i in range(len(io.vector_ref)): + if isinstance(io.vector_ref[i], IONode) is False: + raise TypeError( + "Cannot add IO that is not of type, found {}".format( + io.vector_ref[i] + ) + ) self.FIG.add_fignode(io.vector_ref[i]) def get_io(self, name: str) -> ModuleIO: @@ -93,7 +99,8 @@ def add_finteraction_custom_interaction( ) -> Interaction: # Check if the item exists # TODO: create finteraction factory method and FluidInteraction - # finteraction = FluidInteraction(fluid1=item, interactiontype=interaction_type, custominteraction= operator) + # finteraction = FluidInteraction(fluid1=item, interactiontype=interaction_type, + # custominteraction= operator) finteraction = FluidProcessInteraction(item, operator) self.FIG.add_interaction(finteraction) return finteraction @@ -125,7 +132,6 @@ def add_fluid_finteraction_interaction( fluid1, finteraction, interaction_type ) - # self.FIG.add_fluid_finteraction_interaction(fluid1, finteraction, new_fluid_interaction) self.FIG.add_interaction(new_fluid_interaction) return new_fluid_interaction @@ -212,18 +218,21 @@ def instantiate_module( # Step 4 - Relabel all the nodes with the prefix defined by # var_name - rename_map = {} + fig_node_rename_map = {} + annotation_rename_map = {} for node in list(fig_copy.nodes): - rename_map[node] = self.__generate_instance_node_name(node, var_name) + fig_node_rename_map[node] = self.__generate_instance_node_name( + node, var_name + ) # Step 4.1 - Relabel all the annotations with the prefix defined by var_name for annotation in list(fig_copy.annotations): - rename_map[annotation.id] = self.__generate_instance_node_name( + annotation_rename_map[annotation.id] = self.__generate_instance_node_name( annotation.id, var_name ) - fig_copy.rename_nodes(rename_map) - fig_copy.rename_annotations(rename_map) + fig_copy.rename_nodes(fig_node_rename_map) + fig_copy.rename_annotations(annotation_rename_map) # Step 5 - Stitch together tall the io newly formed io nodes into # current fig @@ -234,7 +243,7 @@ def instantiate_module( # target_fig = self.FIG.get_fignode(rename_map[value]) # source_fig = self.FIG.get_fignode(key) there_check_node = module_to_import.FIG.get_fignode(there_id) - there_node = self.FIG.get_fignode(rename_map[there_id]) + there_node = self.FIG.get_fignode(fig_node_rename_map[there_id]) here_node = self.FIG.get_fignode(here_id) if ( isinstance(there_check_node, IONode) @@ -270,19 +279,19 @@ def instantiate_module( ): # Swap the basic node from original to the instance there_node_id = mapping_instance.node.ID - here_node = self.FIG.get_fignode(rename_map[there_node_id]) + here_node = self.FIG.get_fignode(fig_node_rename_map[there_node_id]) mapping_instance.node = here_node elif isinstance(mapping_instance, NetworkMapping): # TODO - Swap the nodes in the inputs and the outputs # Swap the inputs nodes_to_switch = mapping_instance.input_nodes mapping_instance.input_nodes = self.__switch_fignodes_list( - rename_map, nodes_to_switch + fig_node_rename_map, nodes_to_switch ) nodes_to_switch = mapping_instance.output_nodes mapping_instance.output_nodes = self.__switch_fignodes_list( - rename_map, nodes_to_switch + fig_node_rename_map, nodes_to_switch ) self.mappings.append(mappingtemplate_copy) diff --git a/lfr/fig/fluidinteractiongraph.py b/lfr/fig/fluidinteractiongraph.py index 156a226..4c1a521 100644 --- a/lfr/fig/fluidinteractiongraph.py +++ b/lfr/fig/fluidinteractiongraph.py @@ -220,12 +220,11 @@ def add_not_annotation( return annotation def add_fig(self, fig_to_add: FluidInteractionGraph) -> None: - # Check if any of the incoming fig nodes + # Check if any of the incoming fig nodes are already present here for node_id in fig_to_add.nodes: - fig_node = fig_to_add.get_fignode(node_id) - assert fig_node is not None - # Check if fignode is alreay present in this - self.add_fignode(fig_node) + if node_id in self._fignodes.keys(): + raise Exception("Node '{}' already present in the FIG".format(node_id)) + self.add_fignode(fig_to_add.get_fignode(node_id)) for edge in fig_to_add.edges: self.add_edge(edge[0], edge[1]) @@ -258,24 +257,24 @@ def __deepcopy__(self, memo=None): print("ALREADY COPIED TO", repr(existing)) return existing - fignodes_copy = [] + fignodes_copy_list = [] # Map old_fignode <-> new_fignode fignodes_copy_map: Dict[FIGNode, FIGNode] = dict() for fignode in self._fignodes.values(): fignode_copy = copy.copy(fignode) - fignodes_copy.append(fignode_copy) + fignodes_copy_list.append(fignode_copy) fignodes_copy_map[fignode] = fignode_copy fig_copy = self.copy(as_view=False) fig_copy.__class__ = FluidInteractionGraph assert isinstance(fig_copy, FluidInteractionGraph) - fig_copy.load_fignodes(fignodes_copy) + fig_copy.load_fignodes(fignodes_copy_list) # Now since all the annotations are loaded up, copy the right cloned # references to fig nodes for the constriants - figannotations_copy = [] + figannotations_copy_list = [] # Map old_annotatin <-> new_annotation figannotations_copy_map: Dict[ @@ -284,8 +283,9 @@ def __deepcopy__(self, memo=None): # Copy the annotations into the new fig copy for current_annotation in self._annotations: - copy_annotation = copy.copy(current_annotation) - figannotations_copy.append(copy_annotation) + copy_annotation = copy.deepcopy(current_annotation) + copy_annotation.clear_fignodes() + figannotations_copy_list.append(copy_annotation) figannotations_copy_map[current_annotation] = copy_annotation # TODO - Copy the annotations items @@ -302,7 +302,7 @@ def __deepcopy__(self, memo=None): ) copy_annotation.add_annotated_item(item_to_add) - fig_copy.load_annotations(figannotations_copy) + fig_copy.load_annotations(figannotations_copy_list) return fig_copy # ---------- HELPER METHODS ----------- From 19f4ad2c2b10ef15b05ca8fbf8ed00bbd2a1cc6e Mon Sep 17 00:00:00 2001 From: Radhakrishna Sanka Date: Thu, 15 Jul 2021 19:55:39 -0400 Subject: [PATCH 27/36] Added check to make sure that the pre-processor would ensure that if the pre-load argument is null, it wont crash Added check to ensure that the condition check doesn't crash when theres a BitVector that is passed to the distributeCondition doesn't kill program --- lfr/distBlockListener.py | 16 ++++++++++++++-- lfr/preprocessor.py | 19 ++++++++++--------- 2 files changed, 24 insertions(+), 11 deletions(-) diff --git a/lfr/distBlockListener.py b/lfr/distBlockListener.py index 3902ee9..43a2857 100644 --- a/lfr/distBlockListener.py +++ b/lfr/distBlockListener.py @@ -23,6 +23,9 @@ def exitDistributeCondition(self, ctx: lfrXParser.DistributeConditionContext): rhs = self.stack.pop() lhs = self.stack.pop() + if isinstance(rhs, BitVector): + rhs = rhs.intValue() + relation_operator = ctx.binary_module_path_operator().getText() # TODO - Basically we need to know what the conditions are we need to set @@ -32,8 +35,17 @@ def exitDistributeCondition(self, ctx: lfrXParser.DistributeConditionContext): # this needs to be extended to work with all kinds of conditions # not just 1 variable and the values of 0 or 1. assert len(lhs) == 1 - assert rhs == 0 or rhs == 1 - assert relation_operator == "==" + if relation_operator != "==": + raise NotImplementedError( + 'Did not implement distribute condition beyond "=="' + ) + + if type(rhs) == float or type(rhs) == int: + if (rhs == 0 or rhs == 1) is False: + raise NotImplementedError( + "Did not implement distribute condition to be beyond simple 1 or 0" + " conditions" + ) if self._current_dist_block is None: raise ValueError('"_current_dist_block" is set to None') diff --git a/lfr/preprocessor.py b/lfr/preprocessor.py index 00a20b6..44353b5 100644 --- a/lfr/preprocessor.py +++ b/lfr/preprocessor.py @@ -21,15 +21,16 @@ def __init__(self, file_list: List[str], lib_dir_list: List[str] = []) -> None: self._lib_file_list: Dict[str, str] = {} # Stores file path to file print("Loading all LFR Files from lib Directories:") - for dir_ref in lib_dir_list: - print("-- Loading form path {}".format(dir_ref)) - path = Path(dir_ref).resolve() - - for file in path.rglob("*.lfr"): - path_object = Path(file) - full_path = path_object.resolve() - print("Storing into library: {}".format(full_path)) - self._lib_file_list[str(path_object.name)] = str(full_path) + if lib_dir_list is not None: + for dir_ref in lib_dir_list: + print("-- Loading form path {}".format(dir_ref)) + path = Path(dir_ref).resolve() + + for file in path.rglob("*.lfr"): + path_object = Path(file) + full_path = path_object.resolve() + print("Storing into library: {}".format(full_path)) + self._lib_file_list[str(path_object.name)] = str(full_path) for file_path in file_list: From 7866d3564fd872bfbc2764f5d395d9c80c7f02e7 Mon Sep 17 00:00:00 2001 From: Radhakrishna Sanka Date: Thu, 15 Jul 2021 23:32:06 -0400 Subject: [PATCH 28/36] Fixed the renaming of the figannotations that was throwing an error --- lfr/compiler/module.py | 11 +++--- lfr/fig/annotation.py | 2 +- lfr/fig/fluidinteractiongraph.py | 63 +++++++++++++++++++++++++++++--- 3 files changed, 64 insertions(+), 12 deletions(-) diff --git a/lfr/compiler/module.py b/lfr/compiler/module.py index adeaee1..a186668 100644 --- a/lfr/compiler/module.py +++ b/lfr/compiler/module.py @@ -204,14 +204,13 @@ def instantiate_module( for there_node_key in io_mapping.keys(): fignode = fig_copy.get_fignode(there_node_key) # Skip if its a control type one - assert isinstance(fignode, IONode) + if isinstance(fignode, IONode) is False: + raise TypeError("Node not of type IO Node") + if fignode.type is IOType.CONTROL: continue + # Convert this node into a flow node - # Sanity check to see if its flow input/output - assert ( - fignode.type is IOType.FLOW_INPUT or fignode.type is IOType.FLOW_OUTPUT - ) # Replace new_fignode = Flow(fignode.ID) fig_copy.switch_fignode(fignode, new_fignode) @@ -232,7 +231,7 @@ def instantiate_module( ) fig_copy.rename_nodes(fig_node_rename_map) - fig_copy.rename_annotations(annotation_rename_map) + fig_copy.rename_annotations(fig_node_rename_map, annotation_rename_map) # Step 5 - Stitch together tall the io newly formed io nodes into # current fig diff --git a/lfr/fig/annotation.py b/lfr/fig/annotation.py index 086bb1d..9af8830 100644 --- a/lfr/fig/annotation.py +++ b/lfr/fig/annotation.py @@ -30,7 +30,7 @@ def remove_item( def get_items(self) -> List[Union[Tuple[FIGNode, FIGNode], DistributeAnnotation]]: return self._annotated_items - def clear_fignodes(self) -> None: + def clear_items(self) -> None: self._annotated_items.clear() def __hash__(self) -> int: diff --git a/lfr/fig/fluidinteractiongraph.py b/lfr/fig/fluidinteractiongraph.py index 4c1a521..cf38f9a 100644 --- a/lfr/fig/fluidinteractiongraph.py +++ b/lfr/fig/fluidinteractiongraph.py @@ -52,6 +52,12 @@ def add_state_table(self, state_table) -> None: def annotations(self) -> List[DistributeAnnotation]: return self._annotations + def get_annotation_by_id(self, id: str) -> DistributeAnnotation: + for annotation in self._annotations: + if annotation.id == id: + return annotation + raise Exception("Cannot find the annotation with ID: {0}".format(id)) + def add_fignode(self, node: FIGNode) -> None: self._fignodes[node.ID] = node self._annotations_reverse_map[node] = [] @@ -74,7 +80,7 @@ def load_fignodes(self, fig_nodes: List[FIGNode]) -> None: def load_annotations(self, annotations: List[DistributeAnnotation]) -> None: self._annotations.extend(annotations) - for annotation in self._annotations: + for annotation in annotations: for item in annotation.get_items(): if isinstance(item, DistributeAnnotation): self.__add_to_reverse_map(item, annotation) @@ -99,9 +105,53 @@ def rename_nodes(self, rename_map: Dict[str, str]) -> None: nx.relabel_nodes(self, rename_map, False) - def rename_annotations(self, rename_map: Dict[str, str]) -> None: + def rename_annotations( + self, fig_node_rename_map: Dict[str, str], annotation_rename_map: Dict[str, str] + ) -> None: for annotation in self._annotations: - annotation.rename(rename_map[annotation.id]) + annotation.rename(annotation_rename_map[annotation.id]) + + fig_copy = self + # Switch all the items of the annotations to the new fignodes + for annotation in list(fig_copy.annotations): + new_items_list: List[ + Union[Tuple[FIGNode, FIGNode], DistributeAnnotation] + ] = [] + + old_fignode_item_ID_list = [ + (item[0].ID, item[1].ID) + for item in annotation.get_items() + if type(item) is tuple + ] + + # Now switch the items to the new fignodes + for ( + old_fignode_item_id_1, + old_fignode_item_id_2, + ) in old_fignode_item_ID_list: + new_fignode_item_id_1 = fig_node_rename_map[old_fignode_item_id_1] + new_fignode_item_id_2 = fig_node_rename_map[old_fignode_item_id_2] + item_to_add = ( + fig_copy.get_fignode(new_fignode_item_id_1), + fig_copy.get_fignode(new_fignode_item_id_2), + ) + new_items_list.append(item_to_add) + + old_annotation_item_ID_list = [ + item.id + for item in annotation.get_items() + if isinstance(item, DistributeAnnotation) + ] + + for old_annotation_item_id in old_annotation_item_ID_list: + new_annotation_item_id = annotation_rename_map[old_annotation_item_id] + item_to_add = fig_copy.get_annotation_by_id(new_annotation_item_id) + new_items_list.append(item_to_add) + + # now replace the annotation items with the new ones + annotation.clear_items() + for item in new_items_list: + annotation.add_annotated_item(item) def add_interaction(self, interaction: Interaction): if interaction.ID not in self._fignodes.keys(): @@ -284,7 +334,7 @@ def __deepcopy__(self, memo=None): # Copy the annotations into the new fig copy for current_annotation in self._annotations: copy_annotation = copy.deepcopy(current_annotation) - copy_annotation.clear_fignodes() + copy_annotation.clear_items() figannotations_copy_list.append(copy_annotation) figannotations_copy_map[current_annotation] = copy_annotation @@ -319,8 +369,11 @@ def __add_to_reverse_map( self.__add_to_reverse_map(item, annotation) else: if item in self._annotations_reverse_map.keys(): - self._annotations_reverse_map[item].append(annotation) + annotation_list = self._annotations_reverse_map[item] + if annotation not in annotation_list: + annotation_list.append(annotation) else: + if annotation in self._annotations_reverse_map[item]: raise Exception("Annotation already present in the reverse map !") From ec33b89216fd3137ab40befaa94fa412f717f4e1 Mon Sep 17 00:00:00 2001 From: Radhakrishna Sanka Date: Fri, 16 Jul 2021 11:56:59 -0400 Subject: [PATCH 29/36] Added art for generating ASCII Art, fixed post-rebase errors, fixed issue where module IO errors were being generated because they were getting read correctly. --- lfr/cmdline.py | 4 +- lfr/compiler/lfrerror.py | 1 + lfr/moduleinstanceListener.py | 17 +- .../gen_strategies/marsstrategy.py | 2 - poetry.lock | 1095 +---------------- pyproject.toml | 2 + 6 files changed, 63 insertions(+), 1058 deletions(-) diff --git a/lfr/cmdline.py b/lfr/cmdline.py index d15608f..5f9954e 100644 --- a/lfr/cmdline.py +++ b/lfr/cmdline.py @@ -14,13 +14,13 @@ from lfr.antlrgen.lfr.lfrXParser import lfrXParser from lfr.netlistgenerator.mappinglibrary import MappingLibrary from lfr.netlistgenerator.generator import ( - generate_dropx_library, generate, generate_dropx_library, generate_mars_library, ) from lfr.utils import print_netlist, printgraph, serialize_netlist from lfr.preprocessor import PreProcessor +from art import tprint def load_libraries(): @@ -35,6 +35,8 @@ def load_libraries(): def main(): + # Print LFR ASCII Banner + tprint("---- LFR ----") parser = argparse.ArgumentParser() parser.add_argument( diff --git a/lfr/compiler/lfrerror.py b/lfr/compiler/lfrerror.py index 5f788e4..e550737 100644 --- a/lfr/compiler/lfrerror.py +++ b/lfr/compiler/lfrerror.py @@ -8,6 +8,7 @@ class ErrorType(Enum): VARIABLE_NOT_RECOGNIZED = 30 SIGNAL_NOT_FOUND = 40 MODULE_NOT_FOUND = 50 + MODULE_SIGNAL_BINDING_MISMATCH = 60 class LFRError: diff --git a/lfr/moduleinstanceListener.py b/lfr/moduleinstanceListener.py index 7fbf5b9..6b08741 100644 --- a/lfr/moduleinstanceListener.py +++ b/lfr/moduleinstanceListener.py @@ -1,3 +1,4 @@ +from os import error from typing import Dict, Optional from lfr.compiler.lfrerror import ErrorType, LFRError @@ -80,11 +81,14 @@ def exitOrderedioblock(self, ctx: lfrXParser.OrderedioblockContext): def exitExplicitinstanceiomapping( self, ctx: lfrXParser.ExplicitinstanceiomappingContext ): + if self._module_to_import is None: + raise error("No module to import found in the listener store") + variable = self.stack.pop() label = ctx.ID().getText() # Check if label exists in module_to_import - if label not in self._module_to_import.get_all_io(): + if label not in [item.id for item in self._module_to_import.get_all_io()]: self.compilingErrors.append( LFRError( ErrorType.MODULE_IO_NOT_FOUND, @@ -96,6 +100,13 @@ def exitExplicitinstanceiomapping( return io = self._module_to_import.get_io(label) - assert len(io.vector_ref) == len(variable) + if len(io.vector_ref) != len(variable): + self.compilingErrors.append( + LFRError( + ErrorType.MODULE_SIGNAL_BINDING_MISMATCH, + "Number of module instance signals and variables don't match", + ) + ) + return for i in range(len(variable)): - self._io_mapping[io.vector_ref[i].id] = variable[i].id + self._io_mapping[io.vector_ref[i].ID] = variable[i].ID diff --git a/lfr/netlistgenerator/gen_strategies/marsstrategy.py b/lfr/netlistgenerator/gen_strategies/marsstrategy.py index fe426e4..bf0ac70 100644 --- a/lfr/netlistgenerator/gen_strategies/marsstrategy.py +++ b/lfr/netlistgenerator/gen_strategies/marsstrategy.py @@ -23,8 +23,6 @@ from pymint.mintnode import MINTNode from pymint.minttarget import MINTTarget -from lfr.netlistgenerator.v2.connectingoption import ConnectingOption - class MarsStrategy: def __init__( diff --git a/poetry.lock b/poetry.lock index 529fedb..0f390d5 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,14 +1,3 @@ -[[package]] -name = "absl-py" -version = "0.13.0" -description = "Abseil Python Common Libraries, see https://github.com/abseil/abseil-py." -category = "dev" -optional = false -python-versions = "*" - -[package.dependencies] -six = "*" - [[package]] name = "antlr4-python3-runtime" version = "4.8" @@ -34,15 +23,15 @@ optional = false python-versions = "*" [[package]] -name = "astunparse" -version = "1.6.3" -description = "An AST unparser for Python" -category = "dev" +name = "art" +version = "5.2" +description = "ASCII Art Library For Python" +category = "main" optional = false -python-versions = "*" +python-versions = ">=2.7" -[package.dependencies] -six = ">=1.6.1,<2.0" +[package.extras] +dev = ["coverage (>=4.1)", "codecov (>=2.0.15)", "vulture (>=1.0)", "bandit (>=1.5.1)", "pydocstyle (>=3.0.0)"] [[package]] name = "attrs" @@ -80,30 +69,6 @@ typing-extensions = ">=3.7.4" colorama = ["colorama (>=0.4.3)"] d = ["aiohttp (>=3.3.2)", "aiohttp-cors"] -[[package]] -name = "cachetools" -version = "4.2.2" -description = "Extensible memoizing collections and decorators" -category = "dev" -optional = false -python-versions = "~=3.5" - -[[package]] -name = "certifi" -version = "2021.5.30" -description = "Python package for providing Mozilla's CA Bundle." -category = "dev" -optional = false -python-versions = "*" - -[[package]] -name = "chardet" -version = "4.0.0" -description = "Universal encoding detector for Python 2 and 3" -category = "dev" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" - [[package]] name = "click" version = "7.1.2" @@ -112,22 +77,11 @@ category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" -[[package]] -name = "cycler" -version = "0.10.0" -description = "Composable style cycles" -category = "dev" -optional = false -python-versions = "*" - -[package.dependencies] -six = "*" - [[package]] name = "dafd" version = "0.1.1" description = "Python package for the library for DAFD." -category = "dev" +category = "main" optional = false python-versions = "^3.8" develop = true @@ -168,103 +122,6 @@ mccabe = ">=0.6.0,<0.7.0" pycodestyle = ">=2.6.0a1,<2.7.0" pyflakes = ">=2.2.0,<2.3.0" -[[package]] -name = "flatbuffers" -version = "1.12" -description = "The FlatBuffers serialization format for Python" -category = "dev" -optional = false -python-versions = "*" - -[[package]] -name = "gast" -version = "0.4.0" -description = "Python AST that abstracts the underlying Python version" -category = "dev" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" - -[[package]] -name = "google-auth" -version = "1.31.0" -description = "Google Authentication Library" -category = "dev" -optional = false -python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*" - -[package.dependencies] -cachetools = ">=2.0.0,<5.0" -pyasn1-modules = ">=0.2.1" -rsa = {version = ">=3.1.4,<5", markers = "python_version >= \"3.6\""} -six = ">=1.9.0" - -[package.extras] -aiohttp = ["requests (>=2.20.0,<3.0.0dev)", "aiohttp (>=3.6.2,<4.0.0dev)"] -pyopenssl = ["pyopenssl (>=20.0.0)"] -reauth = ["pyu2f (>=0.1.5)"] - -[[package]] -name = "google-auth-oauthlib" -version = "0.4.4" -description = "Google Authentication Library" -category = "dev" -optional = false -python-versions = ">=3.6" - -[package.dependencies] -google-auth = ">=1.0.0" -requests-oauthlib = ">=0.7.0" - -[package.extras] -tool = ["click (>=6.0.0)"] - -[[package]] -name = "google-pasta" -version = "0.2.0" -description = "pasta is an AST-based Python refactoring library" -category = "dev" -optional = false -python-versions = "*" - -[package.dependencies] -six = "*" - -[[package]] -name = "grpcio" -version = "1.34.1" -description = "HTTP/2-based RPC framework" -category = "dev" -optional = false -python-versions = "*" - -[package.dependencies] -six = ">=1.5.2" - -[package.extras] -protobuf = ["grpcio-tools (>=1.34.1)"] - -[[package]] -name = "h5py" -version = "3.1.0" -description = "Read and write HDF5 files from Python" -category = "dev" -optional = false -python-versions = ">=3.6" - -[package.dependencies] -numpy = [ - {version = ">=1.17.5", markers = "python_version == \"3.8\""}, - {version = ">=1.19.3", markers = "python_version >= \"3.9\""}, -] - -[[package]] -name = "idna" -version = "2.10" -description = "Internationalized Domain Names in Applications (IDNA)" -category = "dev" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" - [[package]] name = "install" version = "1.3.4" @@ -273,14 +130,6 @@ category = "main" optional = false python-versions = ">=2.7, >=3.5" -[[package]] -name = "joblib" -version = "1.0.1" -description = "Lightweight pipelining with Python functions" -category = "dev" -optional = false -python-versions = ">=3.6" - [[package]] name = "jsonschema" version = "3.2.0" @@ -298,84 +147,6 @@ six = ">=1.11.0" format = ["idna", "jsonpointer (>1.13)", "rfc3987", "strict-rfc3339", "webcolors"] format_nongpl = ["idna", "jsonpointer (>1.13)", "webcolors", "rfc3986-validator (>0.1.0)", "rfc3339-validator"] -[[package]] -name = "keras" -version = "2.4.3" -description = "Deep Learning for humans" -category = "dev" -optional = false -python-versions = "*" - -[package.dependencies] -h5py = "*" -numpy = ">=1.9.1" -pyyaml = "*" -scipy = ">=0.14" - -[package.extras] -tests = ["pytest", "pytest-pep8", "pytest-xdist", "flaky", "pytest-cov", "pandas", "requests", "markdown"] -visualize = ["pydot (>=1.2.4)"] - -[[package]] -name = "keras-nightly" -version = "2.6.0.dev2021061400" -description = "TensorFlow Keras." -category = "dev" -optional = false -python-versions = "*" - -[[package]] -name = "keras-preprocessing" -version = "1.1.2" -description = "Easy data preprocessing and data augmentation for deep learning models" -category = "dev" -optional = false -python-versions = "*" - -[package.dependencies] -numpy = ">=1.9.1" -six = ">=1.9.0" - -[package.extras] -image = ["scipy (>=0.14)", "Pillow (>=5.2.0)"] -pep8 = ["flake8"] -tests = ["pandas", "pillow", "tensorflow", "keras", "pytest", "pytest-xdist", "pytest-cov"] - -[[package]] -name = "kiwisolver" -version = "1.3.1" -description = "A fast implementation of the Cassowary constraint solver" -category = "dev" -optional = false -python-versions = ">=3.6" - -[[package]] -name = "markdown" -version = "3.3.4" -description = "Python implementation of Markdown." -category = "dev" -optional = false -python-versions = ">=3.6" - -[package.extras] -testing = ["coverage", "pyyaml"] - -[[package]] -name = "matplotlib" -version = "3.4.2" -description = "Python plotting package" -category = "dev" -optional = false -python-versions = ">=3.7" - -[package.dependencies] -cycler = ">=0.10" -kiwisolver = ">=1.0.1" -numpy = ">=1.16" -pillow = ">=6.2.0" -pyparsing = ">=2.2.1" -python-dateutil = ">=2.7" - [[package]] name = "mccabe" version = "0.6.1" @@ -440,50 +211,6 @@ category = "main" optional = false python-versions = ">=3.6" -[[package]] -name = "oauthlib" -version = "3.1.1" -description = "A generic, spec-compliant, thorough implementation of the OAuth request-signing logic" -category = "dev" -optional = false -python-versions = ">=3.6" - -[package.extras] -rsa = ["cryptography (>=3.0.0,<4)"] -signals = ["blinker (>=1.4.0)"] -signedtoken = ["cryptography (>=3.0.0,<4)", "pyjwt (>=2.0.0,<3)"] - -[[package]] -name = "opt-einsum" -version = "3.3.0" -description = "Optimizing numpys einsum function" -category = "dev" -optional = false -python-versions = ">=3.5" - -[package.dependencies] -numpy = ">=1.7" - -[package.extras] -docs = ["sphinx (1.2.3)", "sphinxcontrib-napoleon", "sphinx-rtd-theme", "numpydoc"] -tests = ["pytest", "pytest-cov", "pytest-pep8"] - -[[package]] -name = "pandas" -version = "1.2.4" -description = "Powerful data structures for data analysis, time series, and statistics" -category = "dev" -optional = false -python-versions = ">=3.7.1" - -[package.dependencies] -numpy = ">=1.16.5" -python-dateutil = ">=2.7.3" -pytz = ">=2017.3" - -[package.extras] -test = ["pytest (>=5.0.1)", "pytest-xdist", "hypothesis (>=3.58)"] - [[package]] name = "parchmint" version = "0.2.9" @@ -510,44 +237,6 @@ category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" -[[package]] -name = "pillow" -version = "8.2.0" -description = "Python Imaging Library (Fork)" -category = "dev" -optional = false -python-versions = ">=3.6" - -[[package]] -name = "protobuf" -version = "3.17.3" -description = "Protocol Buffers" -category = "dev" -optional = false -python-versions = "*" - -[package.dependencies] -six = ">=1.9" - -[[package]] -name = "pyasn1" -version = "0.4.8" -description = "ASN.1 types and codecs" -category = "dev" -optional = false -python-versions = "*" - -[[package]] -name = "pyasn1-modules" -version = "0.2.8" -description = "A collection of ASN.1-based protocols modules." -category = "dev" -optional = false -python-versions = "*" - -[package.dependencies] -pyasn1 = ">=0.4.6,<0.5.0" - [[package]] name = "pycodestyle" version = "2.6.0" @@ -574,64 +263,24 @@ python-versions = ">=3.6" [[package]] name = "pymint" -version = "0.2.7" +version = "0.2.11" description = "MINT is a human readable language to describe Microfluidic Hardware Netlists. MINT is the name of the Microfluidic Netlist language used to describe microfluidic devices for Fluigi to place and route. Mint is a flavor of (MHDL) Microfluidic Hardware Description Language that can be used to represent Microfluidic Circuits." category = "main" optional = false -python-versions = "^3.8" -develop = true +python-versions = ">=3.8,<4.0" [package.dependencies] antlr4-python3-runtime = ">=4.8,<5.0" install = ">=1.3.3,<2.0.0" parchmint = ">=0.2.6,<0.3.0" -[package.source] -type = "directory" -url = "../pymint" - -[[package]] -name = "pyparsing" -version = "2.4.7" -description = "Python parsing module" -category = "dev" -optional = false -python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" - [[package]] name = "pyrsistent" -version = "0.17.3" +version = "0.18.0" description = "Persistent/Functional/Immutable data structures" category = "main" optional = false -python-versions = ">=3.5" - -[[package]] -name = "python-dateutil" -version = "2.8.1" -description = "Extensions to the standard Python datetime module" -category = "dev" -optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" - -[package.dependencies] -six = ">=1.5" - -[[package]] -name = "pytz" -version = "2021.1" -description = "World timezone definitions, modern and historical" -category = "dev" -optional = false -python-versions = "*" - -[[package]] -name = "pyyaml" -version = "5.4.1" -description = "YAML parser and emitter for Python" -category = "dev" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" +python-versions = ">=3.6" [[package]] name = "regex" @@ -641,39 +290,6 @@ category = "dev" optional = false python-versions = "*" -[[package]] -name = "requests" -version = "2.25.1" -description = "Python HTTP for Humans." -category = "dev" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" - -[package.dependencies] -certifi = ">=2017.4.17" -chardet = ">=3.0.2,<5" -idna = ">=2.5,<3" -urllib3 = ">=1.21.1,<1.27" - -[package.extras] -security = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)"] -socks = ["PySocks (>=1.5.6,<1.5.7 || >1.5.7)", "win-inet-pton"] - -[[package]] -name = "requests-oauthlib" -version = "1.3.0" -description = "OAuthlib authentication support for Requests." -category = "dev" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" - -[package.dependencies] -oauthlib = ">=3.0.0" -requests = ">=2.0.0" - -[package.extras] -rsa = ["oauthlib[signedtoken] (>=3.0.0)"] - [[package]] name = "rope" version = "0.18.0" @@ -685,80 +301,9 @@ python-versions = "*" [package.extras] dev = ["pytest"] -[[package]] -name = "rsa" -version = "4.7.2" -description = "Pure-Python RSA implementation" -category = "dev" -optional = false -python-versions = ">=3.5, <4" - -[package.dependencies] -pyasn1 = ">=0.1.3" - -[[package]] -name = "salib" -version = "1.3.13" -description = "Tools for sensitivity analysis. Contains Sobol, Morris, and FAST methods" -category = "dev" -optional = false -python-versions = "*" - -[package.dependencies] -matplotlib = "*" -numpy = "*" -pandas = "*" -scipy = "*" - -[package.extras] -gurobipy = ["gurobipy"] -testing = ["pytest", "pytest-cov"] - -[[package]] -name = "scikit-learn" -version = "0.23.2" -description = "A set of python modules for machine learning and data mining" -category = "dev" -optional = false -python-versions = ">=3.6" - -[package.dependencies] -joblib = ">=0.11" -numpy = ">=1.13.3" -scipy = ">=0.19.1" -threadpoolctl = ">=2.0.0" - -[package.extras] -alldeps = ["numpy (>=1.13.3)", "scipy (>=0.19.1)"] - -[[package]] -name = "scipy" -version = "1.6.1" -description = "SciPy: Scientific Library for Python" -category = "dev" -optional = false -python-versions = ">=3.7" - -[package.dependencies] -numpy = ">=1.16.5" - -[[package]] -name = "seaborn" -version = "0.11.1" -description = "seaborn: statistical data visualization" -category = "dev" -optional = false -python-versions = ">=3.6" - -[package.dependencies] -matplotlib = ">=2.2" -numpy = ">=1.15" -pandas = ">=0.23" -scipy = ">=1.0" - [[package]] name = "six" -version = "1.15.0" +version = "1.16.0" description = "Python 2 and 3 compatibility utilities" category = "main" optional = false @@ -775,95 +320,6 @@ python-versions = "*" [package.extras] widechars = ["wcwidth"] -[[package]] -name = "tensorboard" -version = "2.5.0" -description = "TensorBoard lets you watch Tensors Flow" -category = "dev" -optional = false -python-versions = ">= 2.7, != 3.0.*, != 3.1.*" - -[package.dependencies] -absl-py = ">=0.4" -google-auth = ">=1.6.3,<2" -google-auth-oauthlib = ">=0.4.1,<0.5" -grpcio = ">=1.24.3" -markdown = ">=2.6.8" -numpy = ">=1.12.0" -protobuf = ">=3.6.0" -requests = ">=2.21.0,<3" -tensorboard-data-server = ">=0.6.0,<0.7.0" -tensorboard-plugin-wit = ">=1.6.0" -werkzeug = ">=0.11.15" - -[[package]] -name = "tensorboard-data-server" -version = "0.6.1" -description = "Fast data loading for TensorBoard" -category = "dev" -optional = false -python-versions = ">=3.6" - -[[package]] -name = "tensorboard-plugin-wit" -version = "1.8.0" -description = "What-If Tool TensorBoard plugin." -category = "dev" -optional = false -python-versions = "*" - -[[package]] -name = "tensorflow" -version = "2.5.0" -description = "TensorFlow is an open source machine learning framework for everyone." -category = "dev" -optional = false -python-versions = "*" - -[package.dependencies] -absl-py = ">=0.10,<1.0" -astunparse = ">=1.6.3,<1.7.0" -flatbuffers = ">=1.12.0,<1.13.0" -gast = "0.4.0" -google-pasta = ">=0.2,<1.0" -grpcio = ">=1.34.0,<1.35.0" -h5py = ">=3.1.0,<3.2.0" -keras-nightly = ">=2.5.0.dev,<2.6.0" -keras-preprocessing = ">=1.1.2,<1.2.0" -numpy = ">=1.19.2,<1.20.0" -opt-einsum = ">=3.3.0,<3.4.0" -protobuf = ">=3.9.2" -six = ">=1.15.0,<1.16.0" -tensorboard = ">=2.5,<3.0" -tensorflow-estimator = ">=2.5.0rc0,<2.6.0" -termcolor = ">=1.1.0,<1.2.0" -typing-extensions = ">=3.7.4,<3.8.0" -wrapt = ">=1.12.1,<1.13.0" - -[[package]] -name = "tensorflow-estimator" -version = "2.5.0" -description = "TensorFlow Estimator." -category = "dev" -optional = false -python-versions = "*" - -[[package]] -name = "termcolor" -version = "1.1.0" -description = "ANSII Color formatting for output in terminal." -category = "dev" -optional = false -python-versions = "*" - -[[package]] -name = "threadpoolctl" -version = "2.1.0" -description = "threadpoolctl" -category = "dev" -optional = false -python-versions = ">=3.5" - [[package]] name = "toml" version = "0.10.2" @@ -872,19 +328,6 @@ category = "dev" optional = false python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" -[[package]] -name = "tqdm" -version = "4.61.1" -description = "Fast, Extensible Progress Meter" -category = "dev" -optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.7" - -[package.extras] -dev = ["py-make (>=0.1.0)", "twine", "wheel"] -notebook = ["ipywidgets (>=6)"] -telegram = ["requests"] - [[package]] name = "typed-ast" version = "1.4.1" @@ -901,48 +344,12 @@ category = "dev" optional = false python-versions = "*" -[[package]] -name = "urllib3" -version = "1.26.5" -description = "HTTP library with thread-safe connection pooling, file post, and more." -category = "dev" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4" - -[package.extras] -brotli = ["brotlipy (>=0.6.0)"] -secure = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "certifi", "ipaddress"] -socks = ["PySocks (>=1.5.6,<1.5.7 || >1.5.7,<2.0)"] - -[[package]] -name = "werkzeug" -version = "2.0.1" -description = "The comprehensive WSGI web application library." -category = "dev" -optional = false -python-versions = ">=3.6" - -[package.extras] -watchdog = ["watchdog"] - -[[package]] -name = "wrapt" -version = "1.12.1" -description = "Module for decorators, wrappers and monkey patching." -category = "dev" -optional = false -python-versions = "*" - [metadata] lock-version = "1.1" python-versions = "^3.8" -content-hash = "420af24babeef05b5fd49c1fc0a62467323827140b173d31f4db17a84708f2ef" +content-hash = "47b9e9e748442da58a34fc85a23c06f623e925fb196fb139724244c41b0614b6" [metadata.files] -absl-py = [ - {file = "absl-py-0.13.0.tar.gz", hash = "sha256:6953272383486044699fd0e9f00aad167a27e08ce19aae66c6c4b10e7e767793"}, - {file = "absl_py-0.13.0-py3-none-any.whl", hash = "sha256:62bd4e248ddb19d81aec8f9446b407ff37c8175c2ba88266a7afa9b4ce4a333b"}, -] antlr4-python3-runtime = [ {file = "antlr4-python3-runtime-4.8.tar.gz", hash = "sha256:15793f5d0512a372b4e7d2284058ad32ce7dd27126b105fb0b2245130445db33"}, ] @@ -954,9 +361,9 @@ argparse = [ {file = "argparse-1.4.0-py2.py3-none-any.whl", hash = "sha256:c31647edb69fd3d465a847ea3157d37bed1f95f19760b11a47aa91c04b666314"}, {file = "argparse-1.4.0.tar.gz", hash = "sha256:62b089a55be1d8949cd2bc7e0df0bddb9e028faefc8c32038cc84862aefdd6e4"}, ] -astunparse = [ - {file = "astunparse-1.6.3-py2.py3-none-any.whl", hash = "sha256:c2652417f2c8b5bb325c885ae329bdf3f86424075c4fd1a128674bc6fba4b8e8"}, - {file = "astunparse-1.6.3.tar.gz", hash = "sha256:5ad93a8456f0d084c3456d059fd9a92cce667963232cbf763eac3bc5b7940872"}, +art = [ + {file = "art-5.2-py2.py3-none-any.whl", hash = "sha256:0d289b585a1d36720318383643bde71dc4e10416759486ec800a472091bef1a7"}, + {file = "art-5.2.tar.gz", hash = "sha256:c069b08cd11dec860dab1ce2cc2550ffcf1b76968ff322b4303241e030696ba7"}, ] attrs = [ {file = "attrs-21.2.0-py2.py3-none-any.whl", hash = "sha256:149e90d6d8ac20db7a955ad60cf0e6881a3f20d37096140088356da6c716b0b1"}, @@ -965,26 +372,10 @@ attrs = [ black = [ {file = "black-20.8b1.tar.gz", hash = "sha256:1c02557aa099101b9d21496f8a914e9ed2222ef70336404eeeac8edba836fbea"}, ] -cachetools = [ - {file = "cachetools-4.2.2-py3-none-any.whl", hash = "sha256:2cc0b89715337ab6dbba85b5b50effe2b0c74e035d83ee8ed637cf52f12ae001"}, - {file = "cachetools-4.2.2.tar.gz", hash = "sha256:61b5ed1e22a0924aed1d23b478f37e8d52549ff8a961de2909c69bf950020cff"}, -] -certifi = [ - {file = "certifi-2021.5.30-py2.py3-none-any.whl", hash = "sha256:50b1e4f8446b06f41be7dd6338db18e0990601dce795c2b1686458aa7e8fa7d8"}, - {file = "certifi-2021.5.30.tar.gz", hash = "sha256:2bbf76fd432960138b3ef6dda3dde0544f27cbf8546c458e60baf371917ba9ee"}, -] -chardet = [ - {file = "chardet-4.0.0-py2.py3-none-any.whl", hash = "sha256:f864054d66fd9118f2e67044ac8981a54775ec5b67aed0441892edb553d21da5"}, - {file = "chardet-4.0.0.tar.gz", hash = "sha256:0d6f53a15db4120f2b08c94f11e7d93d2c911ee118b6b30a04ec3ee8310179fa"}, -] click = [ {file = "click-7.1.2-py2.py3-none-any.whl", hash = "sha256:dacca89f4bfadd5de3d7489b7c8a566eee0d3676333fbb50030263894c38c0dc"}, {file = "click-7.1.2.tar.gz", hash = "sha256:d2b5255c7c6349bc1bd1e59e08cd12acbbd63ce649f2588755783aa94dfb6b1a"}, ] -cycler = [ - {file = "cycler-0.10.0-py2.py3-none-any.whl", hash = "sha256:1d8a5ae1ff6c5cf9b93e8811e581232ad8920aeec647c37316ceac982b08cb2d"}, - {file = "cycler-0.10.0.tar.gz", hash = "sha256:cd7b2d1018258d7247a71425e9f26463dfb444d411c39569972f4ce586b0c9d8"}, -] dafd = [] decorator = [ {file = "decorator-4.4.2-py2.py3-none-any.whl", hash = "sha256:41fa54c2a0cc4ba648be4fd43cff00aedf5b9465c9bf18d64325bc225f08f760"}, @@ -994,176 +385,14 @@ flake8 = [ {file = "flake8-3.8.4-py2.py3-none-any.whl", hash = "sha256:749dbbd6bfd0cf1318af27bf97a14e28e5ff548ef8e5b1566ccfb25a11e7c839"}, {file = "flake8-3.8.4.tar.gz", hash = "sha256:aadae8761ec651813c24be05c6f7b4680857ef6afaae4651a4eccaef97ce6c3b"}, ] -flatbuffers = [ - {file = "flatbuffers-1.12-py2.py3-none-any.whl", hash = "sha256:9e9ef47fa92625c4721036e7c4124182668dc6021d9e7c73704edd395648deb9"}, - {file = "flatbuffers-1.12.tar.gz", hash = "sha256:63bb9a722d5e373701913e226135b28a6f6ac200d5cc7b4d919fa38d73b44610"}, -] -gast = [ - {file = "gast-0.4.0-py3-none-any.whl", hash = "sha256:b7adcdd5adbebf1adf17378da5ba3f543684dbec47b1cda1f3997e573cd542c4"}, - {file = "gast-0.4.0.tar.gz", hash = "sha256:40feb7b8b8434785585ab224d1568b857edb18297e5a3047f1ba012bc83b42c1"}, -] -google-auth = [ - {file = "google-auth-1.31.0.tar.gz", hash = "sha256:154f7889c5d679a6f626f36adb12afbd4dbb0a9a04ec575d989d6ba79c4fd65e"}, - {file = "google_auth-1.31.0-py2.py3-none-any.whl", hash = "sha256:6d47c79b5d09fbc7e8355fd9594cc4cf65fdde5d401c63951eaac4baa1ba2ae1"}, -] -google-auth-oauthlib = [ - {file = "google-auth-oauthlib-0.4.4.tar.gz", hash = "sha256:09832c6e75032f93818edf1affe4746121d640c625a5bef9b5c96af676e98eee"}, - {file = "google_auth_oauthlib-0.4.4-py2.py3-none-any.whl", hash = "sha256:0e92aacacfb94978de3b7972cf4b0f204c3cd206f74ddd0dc0b31e91164e6317"}, -] -google-pasta = [ - {file = "google-pasta-0.2.0.tar.gz", hash = "sha256:c9f2c8dfc8f96d0d5808299920721be30c9eec37f2389f28904f454565c8a16e"}, - {file = "google_pasta-0.2.0-py2-none-any.whl", hash = "sha256:4612951da876b1a10fe3960d7226f0c7682cf901e16ac06e473b267a5afa8954"}, - {file = "google_pasta-0.2.0-py3-none-any.whl", hash = "sha256:b32482794a366b5366a32c92a9a9201b107821889935a02b3e51f6b432ea84ed"}, -] -grpcio = [ - {file = "grpcio-1.34.1-cp27-cp27m-macosx_10_10_x86_64.whl", hash = "sha256:5c4402fd8ce28e2847112105591139dc121c8980770f683eb781be1568a64097"}, - {file = "grpcio-1.34.1-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:c6f756c11144c7ecb51b87f0d60a4b72e05635b9f24ddfa004286ab0c8527fa0"}, - {file = "grpcio-1.34.1-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:ec6d1b3daed886a73e40b4dc553474ef415acc111e913d7324cc2c6b0ba9efe0"}, - {file = "grpcio-1.34.1-cp27-cp27m-win32.whl", hash = "sha256:d757bc8bb12f07014dde55a04b5261c94828b605cf0726d02d491c3dc71aa6bb"}, - {file = "grpcio-1.34.1-cp27-cp27m-win_amd64.whl", hash = "sha256:f74cb93cd090b07528cf586a18628370e5780c08e0239f4af796f60a5e773568"}, - {file = "grpcio-1.34.1-cp27-cp27mu-linux_armv7l.whl", hash = "sha256:c4355fa382dfc71c130dc3eccd8ae606a13e1729be2a77b6c44cd5a130d0c616"}, - {file = "grpcio-1.34.1-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:f1a8048428a7a1e5b12322b3ee44ee0bb8e1bea1d67f08fa1813c455f3ef638c"}, - {file = "grpcio-1.34.1-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:0bd906496b9dd3751b9e5cacc7ceb25a57c16ce2aa67315b85ee86a4ba7246f1"}, - {file = "grpcio-1.34.1-cp35-cp35m-linux_armv7l.whl", hash = "sha256:5e488a40ebeb883117aa0dba2cea410ef2ab545a2403b2ac9101e62d42808c71"}, - {file = "grpcio-1.34.1-cp35-cp35m-macosx_10_10_intel.whl", hash = "sha256:98c06f0f7feeca736cc98f3f46b9b74c5f5fdc5febfc7d72728d1895c57be87f"}, - {file = "grpcio-1.34.1-cp35-cp35m-manylinux2010_i686.whl", hash = "sha256:90a4799c15b8b5aa587f65650a0cea28ea88bcd2c5fdf4f1adb2b8b7b4e77a5e"}, - {file = "grpcio-1.34.1-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:121af89d0b9ba1d47c738242783675009dd4e9067359481e4b743eb9e5886682"}, - {file = "grpcio-1.34.1-cp35-cp35m-manylinux2014_i686.whl", hash = "sha256:1be193803c706f78d0df12c817eaf2415fb4d39472fa00d860700e6c7a99f8f7"}, - {file = "grpcio-1.34.1-cp35-cp35m-manylinux2014_x86_64.whl", hash = "sha256:9e465a1d594a9a5f4252c4abbb93909c42768bee5fbfcd18098d60bf06a35573"}, - {file = "grpcio-1.34.1-cp35-cp35m-win32.whl", hash = "sha256:8b16d14160b7fd8bc43600be70e0da677d17dd8aafb5a258bbda996fe410320e"}, - {file = "grpcio-1.34.1-cp35-cp35m-win_amd64.whl", hash = "sha256:8a543209ab606dd55c58dc218be8e8619214607f03717dded78c7d27f1d05ba5"}, - {file = "grpcio-1.34.1-cp36-cp36m-linux_armv7l.whl", hash = "sha256:f74f270550df347a18f839331f84838b938c8923a9e13a6fa7cc69c79087a686"}, - {file = "grpcio-1.34.1-cp36-cp36m-macosx_10_10_x86_64.whl", hash = "sha256:163a2cf7f4df3ff0a04f49e634526e3d88f02393a7ebf8f34a2134c88b06322e"}, - {file = "grpcio-1.34.1-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:11735ac4efd53691afeb36d006e20db9b7d4b6f3356c751f32d5747aee38fa4c"}, - {file = "grpcio-1.34.1-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:79bda20756e2fc7236b94468ffcce4b516953f946a80b7ea883f89d9e9b25a41"}, - {file = "grpcio-1.34.1-cp36-cp36m-manylinux2014_i686.whl", hash = "sha256:1857f88b351e2382aa57ed892960361a8b71acca4aa1b90998007b4177f15114"}, - {file = "grpcio-1.34.1-cp36-cp36m-manylinux2014_x86_64.whl", hash = "sha256:6f81fbf9f830e20aee93480305877f73f15bfa58fa87433eb331696be47ae7ba"}, - {file = "grpcio-1.34.1-cp36-cp36m-win32.whl", hash = "sha256:ff8aef869c2e9de65c3a693406f7d1200d87e6d541d096eae69f98e7f301fa60"}, - {file = "grpcio-1.34.1-cp36-cp36m-win_amd64.whl", hash = "sha256:ece7459c182e00ca90b2e5823940a552651b5eb3acdeee9350377ddb44d9c412"}, - {file = "grpcio-1.34.1-cp37-cp37m-macosx_10_10_x86_64.whl", hash = "sha256:7924ef3a898f6ff985540ee5d8c7554f0c925dc7668c3d63461600ea50b39658"}, - {file = "grpcio-1.34.1-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:b5e96ca83d5c34c9b60d8951e52492b0d9d072c3fe38a1c19765932e121036ce"}, - {file = "grpcio-1.34.1-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:fe9360347a3f4f2ec6923d8afb03a9194f3f14e054cb09e75e8346af9c0aa9f6"}, - {file = "grpcio-1.34.1-cp37-cp37m-manylinux2014_i686.whl", hash = "sha256:cadc09c9bd24ecf3ba7ae55b5a741f7de694a8843e97e82a7c3fa2e6e81e0f9a"}, - {file = "grpcio-1.34.1-cp37-cp37m-manylinux2014_x86_64.whl", hash = "sha256:5971e6dfcfa0ebeb0df2d15383e1b53fa36208198c8aff9a4eed5ece2a6d4571"}, - {file = "grpcio-1.34.1-cp37-cp37m-win32.whl", hash = "sha256:a181092b534e996e36d0c0216d81280d4942322170c823b2fb84ec4597dc0bd5"}, - {file = "grpcio-1.34.1-cp37-cp37m-win_amd64.whl", hash = "sha256:2b97cdd4582445ad7bd441f5f3c57d838bcdc518a05713dab0c7f4b945afb39e"}, - {file = "grpcio-1.34.1-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:ff760c5ce73c177851864e8caaf75467eaf06c1b6857b21e1789658375e720fb"}, - {file = "grpcio-1.34.1-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:fd58ea88dd5439e03c6587f0b672db1627ec8ed47be312c74632650dfed33c2e"}, - {file = "grpcio-1.34.1-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:f6fee4445cffb45593b4c1d9bb0bc7922e77ec846a1237e2e744b1223d69c863"}, - {file = "grpcio-1.34.1-cp38-cp38-manylinux2014_i686.whl", hash = "sha256:cd4da71e105088b1a7e629d1b033f16d87dec08524d0e4f5d77982af6fe1b6c2"}, - {file = "grpcio-1.34.1-cp38-cp38-manylinux2014_x86_64.whl", hash = "sha256:9d43849d8925ec24bf121bccd941a13d4e8c2cffdfa769a04a6d4ed38c6b88a2"}, - {file = "grpcio-1.34.1-cp38-cp38-win32.whl", hash = "sha256:696f0de4d47f738063432bbbcecd07f78256864f0839e41369458421f539f00a"}, - {file = "grpcio-1.34.1-cp38-cp38-win_amd64.whl", hash = "sha256:8fff784ec5d12252a7cc0ab6f1a3206861b94e45ee0ebeba2439bd10a6db2f1a"}, - {file = "grpcio-1.34.1-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:ed8ac4f76cbbef5dc54594cb7bf6fbb985f5be66abcb1f9da8142500e4d76492"}, - {file = "grpcio-1.34.1-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:8dad4184e4669672e126de26776eba8e3db4914660b4a0a6c7edbdbcf3e2f05f"}, - {file = "grpcio-1.34.1-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:011e9b5e47cb9d2a808e8c2dd5ae86df085d5879d9e8095a24631a32c577f231"}, - {file = "grpcio-1.34.1-cp39-cp39-manylinux2014_i686.whl", hash = "sha256:49ffc5bb78b201db24d8d1644193beb50a896c3cb35b259b4fb9c44dba18585f"}, - {file = "grpcio-1.34.1-cp39-cp39-manylinux2014_x86_64.whl", hash = "sha256:cfe0e015cb8db5a27a92621fdd9dc8e69b2f7130db326601802e6ff36626deff"}, - {file = "grpcio-1.34.1-cp39-cp39-win32.whl", hash = "sha256:809732f300fa8093b40f843c36f6f78423ffb40493098185bc4a96bd67126db5"}, - {file = "grpcio-1.34.1-cp39-cp39-win_amd64.whl", hash = "sha256:96dc85c059f15390beb7ac6bf075d1e4cf72e8f5c9b6c37ea179b7cc579816fd"}, - {file = "grpcio-1.34.1.tar.gz", hash = "sha256:1c746a3cd8a830d8d916a9d0476a786aaa98c5cc2a096344af2be955e439f8ac"}, -] -h5py = [ - {file = "h5py-3.1.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:1cd367f89a5441236bdbb795e9fb9a9e3424929c00b4a54254ca760437f83d69"}, - {file = "h5py-3.1.0-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:fea05349f63625a8fb808e57e42bb4c76930cf5d50ac58b678c52f913a48a89b"}, - {file = "h5py-3.1.0-cp36-cp36m-win_amd64.whl", hash = "sha256:2e37352ddfcf9d77a2a47f7c8f7e125c6d20cc06c2995edeb7be222d4e152636"}, - {file = "h5py-3.1.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:e33f61d3eb862614c0f273a1f993a64dc2f093e1a3094932c50ada9d2db2170f"}, - {file = "h5py-3.1.0-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:236ac8d943be30b617ab615c3d4a4bf4a438add2be87e54af3687ab721a18fac"}, - {file = "h5py-3.1.0-cp37-cp37m-win_amd64.whl", hash = "sha256:02c391fdb980762a1cc03a4bcaecd03dc463994a9a63a02264830114a96e111f"}, - {file = "h5py-3.1.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:f89a3dae38843ffa49d17a31a3509a8129e9b46ece602a0138e1ed79e685c361"}, - {file = "h5py-3.1.0-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:ba71f6229d2013fbb606476ecc29c6223fc16b244d35fcd8566ad9dbaf910857"}, - {file = "h5py-3.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:dccb89358bc84abcd711363c3e138f9f4eccfdf866f2139a8e72308328765b2c"}, - {file = "h5py-3.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:cb74df83709d6d03d11e60b9480812f58da34f194beafa8c8314dbbeeedfe0a6"}, - {file = "h5py-3.1.0-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:80c623be10479e81b64fa713b7ed4c0bbe9f02e8e7d2a2e5382336087b615ce4"}, - {file = "h5py-3.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:1cdfd1c5449ca1329d152f0b66830e93226ebce4f5e07dd8dc16bfc2b1a49d7b"}, - {file = "h5py-3.1.0.tar.gz", hash = "sha256:1e2516f190652beedcb8c7acfa1c6fa92d99b42331cbef5e5c7ec2d65b0fc3c2"}, -] -idna = [ - {file = "idna-2.10-py2.py3-none-any.whl", hash = "sha256:b97d804b1e9b523befed77c48dacec60e6dcb0b5391d57af6a65a312a90648c0"}, - {file = "idna-2.10.tar.gz", hash = "sha256:b307872f855b18632ce0c21c5e45be78c0ea7ae4c15c828c20788b26921eb3f6"}, -] install = [ {file = "install-1.3.4-py3-none-any.whl", hash = "sha256:dfec614e0cc90f00659067a078bc2ec032d9f0758f3df230c073eeaffd0cefa1"}, {file = "install-1.3.4.tar.gz", hash = "sha256:1455304a2475b6753a9b0c28243ca7d8b7d71aeea1a88e6de94034fc57d765a0"}, ] -joblib = [ - {file = "joblib-1.0.1-py3-none-any.whl", hash = "sha256:feeb1ec69c4d45129954f1b7034954241eedfd6ba39b5e9e4b6883be3332d5e5"}, - {file = "joblib-1.0.1.tar.gz", hash = "sha256:9c17567692206d2f3fb9ecf5e991084254fe631665c450b443761c4186a613f7"}, -] jsonschema = [ {file = "jsonschema-3.2.0-py2.py3-none-any.whl", hash = "sha256:4e5b3cf8216f577bee9ce139cbe72eca3ea4f292ec60928ff24758ce626cd163"}, {file = "jsonschema-3.2.0.tar.gz", hash = "sha256:c8a85b28d377cc7737e46e2d9f2b4f44ee3c0e1deac6bf46ddefc7187d30797a"}, ] -keras = [ - {file = "Keras-2.4.3-py2.py3-none-any.whl", hash = "sha256:05e2faf6885f7899482a7d18fc00ba9655fe2c9296a35ad96949a07a9c27d1bb"}, - {file = "Keras-2.4.3.tar.gz", hash = "sha256:fedd729b52572fb108a98e3d97e1bac10a81d3917d2103cc20ab2a5f03beb973"}, -] -keras-nightly = [ - {file = "keras_nightly-2.6.0.dev2021061400-py2.py3-none-any.whl", hash = "sha256:43bddfdb966d3927f817e6cbfeff3a2e17c65f864b368b7df0bf04768110f0f8"}, -] -keras-preprocessing = [ - {file = "Keras_Preprocessing-1.1.2-py2.py3-none-any.whl", hash = "sha256:7b82029b130ff61cc99b55f3bd27427df4838576838c5b2f65940e4fcec99a7b"}, - {file = "Keras_Preprocessing-1.1.2.tar.gz", hash = "sha256:add82567c50c8bc648c14195bf544a5ce7c1f76761536956c3d2978970179ef3"}, -] -kiwisolver = [ - {file = "kiwisolver-1.3.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:fd34fbbfbc40628200730bc1febe30631347103fc8d3d4fa012c21ab9c11eca9"}, - {file = "kiwisolver-1.3.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:d3155d828dec1d43283bd24d3d3e0d9c7c350cdfcc0bd06c0ad1209c1bbc36d0"}, - {file = "kiwisolver-1.3.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:5a7a7dbff17e66fac9142ae2ecafb719393aaee6a3768c9de2fd425c63b53e21"}, - {file = "kiwisolver-1.3.1-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:f8d6f8db88049a699817fd9178782867bf22283e3813064302ac59f61d95be05"}, - {file = "kiwisolver-1.3.1-cp36-cp36m-manylinux2014_ppc64le.whl", hash = "sha256:5f6ccd3dd0b9739edcf407514016108e2280769c73a85b9e59aa390046dbf08b"}, - {file = "kiwisolver-1.3.1-cp36-cp36m-win32.whl", hash = "sha256:225e2e18f271e0ed8157d7f4518ffbf99b9450fca398d561eb5c4a87d0986dd9"}, - {file = "kiwisolver-1.3.1-cp36-cp36m-win_amd64.whl", hash = "sha256:cf8b574c7b9aa060c62116d4181f3a1a4e821b2ec5cbfe3775809474113748d4"}, - {file = "kiwisolver-1.3.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:232c9e11fd7ac3a470d65cd67e4359eee155ec57e822e5220322d7b2ac84fbf0"}, - {file = "kiwisolver-1.3.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:b38694dcdac990a743aa654037ff1188c7a9801ac3ccc548d3341014bc5ca278"}, - {file = "kiwisolver-1.3.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:ca3820eb7f7faf7f0aa88de0e54681bddcb46e485beb844fcecbcd1c8bd01689"}, - {file = "kiwisolver-1.3.1-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:c8fd0f1ae9d92b42854b2979024d7597685ce4ada367172ed7c09edf2cef9cb8"}, - {file = "kiwisolver-1.3.1-cp37-cp37m-manylinux2014_ppc64le.whl", hash = "sha256:1e1bc12fb773a7b2ffdeb8380609f4f8064777877b2225dec3da711b421fda31"}, - {file = "kiwisolver-1.3.1-cp37-cp37m-win32.whl", hash = "sha256:72c99e39d005b793fb7d3d4e660aed6b6281b502e8c1eaf8ee8346023c8e03bc"}, - {file = "kiwisolver-1.3.1-cp37-cp37m-win_amd64.whl", hash = "sha256:8be8d84b7d4f2ba4ffff3665bcd0211318aa632395a1a41553250484a871d454"}, - {file = "kiwisolver-1.3.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:31dfd2ac56edc0ff9ac295193eeaea1c0c923c0355bf948fbd99ed6018010b72"}, - {file = "kiwisolver-1.3.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:563c649cfdef27d081c84e72a03b48ea9408c16657500c312575ae9d9f7bc1c3"}, - {file = "kiwisolver-1.3.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:78751b33595f7f9511952e7e60ce858c6d64db2e062afb325985ddbd34b5c131"}, - {file = "kiwisolver-1.3.1-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:a357fd4f15ee49b4a98b44ec23a34a95f1e00292a139d6015c11f55774ef10de"}, - {file = "kiwisolver-1.3.1-cp38-cp38-manylinux2014_ppc64le.whl", hash = "sha256:5989db3b3b34b76c09253deeaf7fbc2707616f130e166996606c284395da3f18"}, - {file = "kiwisolver-1.3.1-cp38-cp38-win32.whl", hash = "sha256:c08e95114951dc2090c4a630c2385bef681cacf12636fb0241accdc6b303fd81"}, - {file = "kiwisolver-1.3.1-cp38-cp38-win_amd64.whl", hash = "sha256:44a62e24d9b01ba94ae7a4a6c3fb215dc4af1dde817e7498d901e229aaf50e4e"}, - {file = "kiwisolver-1.3.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:50af681a36b2a1dee1d3c169ade9fdc59207d3c31e522519181e12f1b3ba7000"}, - {file = "kiwisolver-1.3.1-cp39-cp39-manylinux1_i686.whl", hash = "sha256:a53d27d0c2a0ebd07e395e56a1fbdf75ffedc4a05943daf472af163413ce9598"}, - {file = "kiwisolver-1.3.1-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:834ee27348c4aefc20b479335fd422a2c69db55f7d9ab61721ac8cd83eb78882"}, - {file = "kiwisolver-1.3.1-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:5c3e6455341008a054cccee8c5d24481bcfe1acdbc9add30aa95798e95c65621"}, - {file = "kiwisolver-1.3.1-cp39-cp39-manylinux2014_ppc64le.whl", hash = "sha256:acef3d59d47dd85ecf909c359d0fd2c81ed33bdff70216d3956b463e12c38a54"}, - {file = "kiwisolver-1.3.1-cp39-cp39-win32.whl", hash = "sha256:c5518d51a0735b1e6cee1fdce66359f8d2b59c3ca85dc2b0813a8aa86818a030"}, - {file = "kiwisolver-1.3.1-cp39-cp39-win_amd64.whl", hash = "sha256:b9edd0110a77fc321ab090aaa1cfcaba1d8499850a12848b81be2222eab648f6"}, - {file = "kiwisolver-1.3.1-pp36-pypy36_pp73-macosx_10_9_x86_64.whl", hash = "sha256:0cd53f403202159b44528498de18f9285b04482bab2a6fc3f5dd8dbb9352e30d"}, - {file = "kiwisolver-1.3.1-pp36-pypy36_pp73-manylinux2010_x86_64.whl", hash = "sha256:33449715e0101e4d34f64990352bce4095c8bf13bed1b390773fc0a7295967b3"}, - {file = "kiwisolver-1.3.1-pp36-pypy36_pp73-win32.whl", hash = "sha256:401a2e9afa8588589775fe34fc22d918ae839aaaf0c0e96441c0fdbce6d8ebe6"}, - {file = "kiwisolver-1.3.1.tar.gz", hash = "sha256:950a199911a8d94683a6b10321f9345d5a3a8433ec58b217ace979e18f16e248"}, -] -markdown = [ - {file = "Markdown-3.3.4-py3-none-any.whl", hash = "sha256:96c3ba1261de2f7547b46a00ea8463832c921d3f9d6aba3f255a6f71386db20c"}, - {file = "Markdown-3.3.4.tar.gz", hash = "sha256:31b5b491868dcc87d6c24b7e3d19a0d730d59d3e46f4eea6430a321bed387a49"}, -] -matplotlib = [ - {file = "matplotlib-3.4.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c541ee5a3287efe066bbe358320853cf4916bc14c00c38f8f3d8d75275a405a9"}, - {file = "matplotlib-3.4.2-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:3a5c18dbd2c7c366da26a4ad1462fe3e03a577b39e3b503bbcf482b9cdac093c"}, - {file = "matplotlib-3.4.2-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:a9d8cb5329df13e0cdaa14b3b43f47b5e593ec637f13f14db75bb16e46178b05"}, - {file = "matplotlib-3.4.2-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:7ad19f3fb6145b9eb41c08e7cbb9f8e10b91291396bee21e9ce761bb78df63ec"}, - {file = "matplotlib-3.4.2-cp37-cp37m-win32.whl", hash = "sha256:7a58f3d8fe8fac3be522c79d921c9b86e090a59637cb88e3bc51298d7a2c862a"}, - {file = "matplotlib-3.4.2-cp37-cp37m-win_amd64.whl", hash = "sha256:6382bc6e2d7e481bcd977eb131c31dee96e0fb4f9177d15ec6fb976d3b9ace1a"}, - {file = "matplotlib-3.4.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6a6a44f27aabe720ec4fd485061e8a35784c2b9ffa6363ad546316dfc9cea04e"}, - {file = "matplotlib-3.4.2-cp38-cp38-manylinux1_i686.whl", hash = "sha256:1c1779f7ab7d8bdb7d4c605e6ffaa0614b3e80f1e3c8ccf7b9269a22dbc5986b"}, - {file = "matplotlib-3.4.2-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:5826f56055b9b1c80fef82e326097e34dc4af8c7249226b7dd63095a686177d1"}, - {file = "matplotlib-3.4.2-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:0bea5ec5c28d49020e5d7923c2725b837e60bc8be99d3164af410eb4b4c827da"}, - {file = "matplotlib-3.4.2-cp38-cp38-win32.whl", hash = "sha256:6475d0209024a77f869163ec3657c47fed35d9b6ed8bccba8aa0f0099fbbdaa8"}, - {file = "matplotlib-3.4.2-cp38-cp38-win_amd64.whl", hash = "sha256:21b31057bbc5e75b08e70a43cefc4c0b2c2f1b1a850f4a0f7af044eb4163086c"}, - {file = "matplotlib-3.4.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b26535b9de85326e6958cdef720ecd10bcf74a3f4371bf9a7e5b2e659c17e153"}, - {file = "matplotlib-3.4.2-cp39-cp39-manylinux1_i686.whl", hash = "sha256:32fa638cc10886885d1ca3d409d4473d6a22f7ceecd11322150961a70fab66dd"}, - {file = "matplotlib-3.4.2-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:956c8849b134b4a343598305a3ca1bdd3094f01f5efc8afccdebeffe6b315247"}, - {file = "matplotlib-3.4.2-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:85f191bb03cb1a7b04b5c2cca4792bef94df06ef473bc49e2818105671766fee"}, - {file = "matplotlib-3.4.2-cp39-cp39-win32.whl", hash = "sha256:b1d5a2cedf5de05567c441b3a8c2651fbde56df08b82640e7f06c8cd91e201f6"}, - {file = "matplotlib-3.4.2-cp39-cp39-win_amd64.whl", hash = "sha256:df815378a754a7edd4559f8c51fc7064f779a74013644a7f5ac7a0c31f875866"}, - {file = "matplotlib-3.4.2.tar.gz", hash = "sha256:d8d994cefdff9aaba45166eb3de4f5211adb4accac85cbf97137e98f26ea0219"}, -] mccabe = [ {file = "mccabe-0.6.1-py2.py3-none-any.whl", hash = "sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42"}, {file = "mccabe-0.6.1.tar.gz", hash = "sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f"}, @@ -1228,128 +457,11 @@ numpy = [ {file = "numpy-1.19.5-pp36-pypy36_pp73-manylinux2010_x86_64.whl", hash = "sha256:a0d53e51a6cb6f0d9082decb7a4cb6dfb33055308c4c44f53103c073f649af73"}, {file = "numpy-1.19.5.zip", hash = "sha256:a76f502430dd98d7546e1ea2250a7360c065a5fdea52b2dffe8ae7180909b6f4"}, ] -oauthlib = [ - {file = "oauthlib-3.1.1-py2.py3-none-any.whl", hash = "sha256:42bf6354c2ed8c6acb54d971fce6f88193d97297e18602a3a886603f9d7730cc"}, - {file = "oauthlib-3.1.1.tar.gz", hash = "sha256:8f0215fcc533dd8dd1bee6f4c412d4f0cd7297307d43ac61666389e3bc3198a3"}, -] -opt-einsum = [ - {file = "opt_einsum-3.3.0-py3-none-any.whl", hash = "sha256:2455e59e3947d3c275477df7f5205b30635e266fe6dc300e3d9f9646bfcea147"}, - {file = "opt_einsum-3.3.0.tar.gz", hash = "sha256:59f6475f77bbc37dcf7cd748519c0ec60722e91e63ca114e68821c0c54a46549"}, -] -pandas = [ - {file = "pandas-1.2.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c601c6fdebc729df4438ec1f62275d6136a0dd14d332fc0e8ce3f7d2aadb4dd6"}, - {file = "pandas-1.2.4-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:8d4c74177c26aadcfb4fd1de6c1c43c2bf822b3e0fc7a9b409eeaf84b3e92aaa"}, - {file = "pandas-1.2.4-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:b730add5267f873b3383c18cac4df2527ac4f0f0eed1c6cf37fcb437e25cf558"}, - {file = "pandas-1.2.4-cp37-cp37m-win32.whl", hash = "sha256:2cb7e8f4f152f27dc93f30b5c7a98f6c748601ea65da359af734dd0cf3fa733f"}, - {file = "pandas-1.2.4-cp37-cp37m-win_amd64.whl", hash = "sha256:2111c25e69fa9365ba80bbf4f959400054b2771ac5d041ed19415a8b488dc70a"}, - {file = "pandas-1.2.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:167693a80abc8eb28051fbd184c1b7afd13ce2c727a5af47b048f1ea3afefff4"}, - {file = "pandas-1.2.4-cp38-cp38-manylinux1_i686.whl", hash = "sha256:612add929bf3ba9d27b436cc8853f5acc337242d6b584203f207e364bb46cb12"}, - {file = "pandas-1.2.4-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:971e2a414fce20cc5331fe791153513d076814d30a60cd7348466943e6e909e4"}, - {file = "pandas-1.2.4-cp38-cp38-win32.whl", hash = "sha256:68d7baa80c74aaacbed597265ca2308f017859123231542ff8a5266d489e1858"}, - {file = "pandas-1.2.4-cp38-cp38-win_amd64.whl", hash = "sha256:bd659c11a4578af740782288cac141a322057a2e36920016e0fc7b25c5a4b686"}, - {file = "pandas-1.2.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9db70ffa8b280bb4de83f9739d514cd0735825e79eef3a61d312420b9f16b758"}, - {file = "pandas-1.2.4-cp39-cp39-manylinux1_i686.whl", hash = "sha256:298f0553fd3ba8e002c4070a723a59cdb28eda579f3e243bc2ee397773f5398b"}, - {file = "pandas-1.2.4-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:52d2472acbb8a56819a87aafdb8b5b6d2b3386e15c95bde56b281882529a7ded"}, - {file = "pandas-1.2.4-cp39-cp39-win32.whl", hash = "sha256:d0877407359811f7b853b548a614aacd7dea83b0c0c84620a9a643f180060950"}, - {file = "pandas-1.2.4-cp39-cp39-win_amd64.whl", hash = "sha256:2b063d41803b6a19703b845609c0b700913593de067b552a8b24dd8eeb8c9895"}, - {file = "pandas-1.2.4.tar.gz", hash = "sha256:649ecab692fade3cbfcf967ff936496b0cfba0af00a55dfaacd82bdda5cb2279"}, -] parchmint = [] pathspec = [ {file = "pathspec-0.8.1-py2.py3-none-any.whl", hash = "sha256:aa0cb481c4041bf52ffa7b0d8fa6cd3e88a2ca4879c533c9153882ee2556790d"}, {file = "pathspec-0.8.1.tar.gz", hash = "sha256:86379d6b86d75816baba717e64b1a3a3469deb93bb76d613c9ce79edc5cb68fd"}, ] -pillow = [ - {file = "Pillow-8.2.0-cp36-cp36m-macosx_10_10_x86_64.whl", hash = "sha256:dc38f57d8f20f06dd7c3161c59ca2c86893632623f33a42d592f097b00f720a9"}, - {file = "Pillow-8.2.0-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:a013cbe25d20c2e0c4e85a9daf438f85121a4d0344ddc76e33fd7e3965d9af4b"}, - {file = "Pillow-8.2.0-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:8bb1e155a74e1bfbacd84555ea62fa21c58e0b4e7e6b20e4447b8d07990ac78b"}, - {file = "Pillow-8.2.0-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:c5236606e8570542ed424849f7852a0ff0bce2c4c8d0ba05cc202a5a9c97dee9"}, - {file = "Pillow-8.2.0-cp36-cp36m-win32.whl", hash = "sha256:12e5e7471f9b637762453da74e390e56cc43e486a88289995c1f4c1dc0bfe727"}, - {file = "Pillow-8.2.0-cp36-cp36m-win_amd64.whl", hash = "sha256:5afe6b237a0b81bd54b53f835a153770802f164c5570bab5e005aad693dab87f"}, - {file = "Pillow-8.2.0-cp37-cp37m-macosx_10_10_x86_64.whl", hash = "sha256:cb7a09e173903541fa888ba010c345893cd9fc1b5891aaf060f6ca77b6a3722d"}, - {file = "Pillow-8.2.0-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:0d19d70ee7c2ba97631bae1e7d4725cdb2ecf238178096e8c82ee481e189168a"}, - {file = "Pillow-8.2.0-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:083781abd261bdabf090ad07bb69f8f5599943ddb539d64497ed021b2a67e5a9"}, - {file = "Pillow-8.2.0-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:c6b39294464b03457f9064e98c124e09008b35a62e3189d3513e5148611c9388"}, - {file = "Pillow-8.2.0-cp37-cp37m-win32.whl", hash = "sha256:01425106e4e8cee195a411f729cff2a7d61813b0b11737c12bd5991f5f14bcd5"}, - {file = "Pillow-8.2.0-cp37-cp37m-win_amd64.whl", hash = "sha256:3b570f84a6161cf8865c4e08adf629441f56e32f180f7aa4ccbd2e0a5a02cba2"}, - {file = "Pillow-8.2.0-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:031a6c88c77d08aab84fecc05c3cde8414cd6f8406f4d2b16fed1e97634cc8a4"}, - {file = "Pillow-8.2.0-cp38-cp38-manylinux1_i686.whl", hash = "sha256:66cc56579fd91f517290ab02c51e3a80f581aba45fd924fcdee01fa06e635812"}, - {file = "Pillow-8.2.0-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:6c32cc3145928c4305d142ebec682419a6c0a8ce9e33db900027ddca1ec39178"}, - {file = "Pillow-8.2.0-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:624b977355cde8b065f6d51b98497d6cd5fbdd4f36405f7a8790e3376125e2bb"}, - {file = "Pillow-8.2.0-cp38-cp38-win32.whl", hash = "sha256:5cbf3e3b1014dddc45496e8cf38b9f099c95a326275885199f427825c6522232"}, - {file = "Pillow-8.2.0-cp38-cp38-win_amd64.whl", hash = "sha256:463822e2f0d81459e113372a168f2ff59723e78528f91f0bd25680ac185cf797"}, - {file = "Pillow-8.2.0-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:95d5ef984eff897850f3a83883363da64aae1000e79cb3c321915468e8c6add5"}, - {file = "Pillow-8.2.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b91c36492a4bbb1ee855b7d16fe51379e5f96b85692dc8210831fbb24c43e484"}, - {file = "Pillow-8.2.0-cp39-cp39-manylinux1_i686.whl", hash = "sha256:d68cb92c408261f806b15923834203f024110a2e2872ecb0bd2a110f89d3c602"}, - {file = "Pillow-8.2.0-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:f217c3954ce5fd88303fc0c317af55d5e0204106d86dea17eb8205700d47dec2"}, - {file = "Pillow-8.2.0-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:5b70110acb39f3aff6b74cf09bb4169b167e2660dabc304c1e25b6555fa781ef"}, - {file = "Pillow-8.2.0-cp39-cp39-win32.whl", hash = "sha256:a7d5e9fad90eff8f6f6106d3b98b553a88b6f976e51fce287192a5d2d5363713"}, - {file = "Pillow-8.2.0-cp39-cp39-win_amd64.whl", hash = "sha256:238c197fc275b475e87c1453b05b467d2d02c2915fdfdd4af126145ff2e4610c"}, - {file = "Pillow-8.2.0-pp36-pypy36_pp73-macosx_10_10_x86_64.whl", hash = "sha256:0e04d61f0064b545b989126197930807c86bcbd4534d39168f4aa5fda39bb8f9"}, - {file = "Pillow-8.2.0-pp36-pypy36_pp73-manylinux2010_i686.whl", hash = "sha256:63728564c1410d99e6d1ae8e3b810fe012bc440952168af0a2877e8ff5ab96b9"}, - {file = "Pillow-8.2.0-pp36-pypy36_pp73-manylinux2010_x86_64.whl", hash = "sha256:c03c07ed32c5324939b19e36ae5f75c660c81461e312a41aea30acdd46f93a7c"}, - {file = "Pillow-8.2.0-pp37-pypy37_pp73-macosx_10_10_x86_64.whl", hash = "sha256:4d98abdd6b1e3bf1a1cbb14c3895226816e666749ac040c4e2554231068c639b"}, - {file = "Pillow-8.2.0-pp37-pypy37_pp73-manylinux2010_i686.whl", hash = "sha256:aac00e4bc94d1b7813fe882c28990c1bc2f9d0e1aa765a5f2b516e8a6a16a9e4"}, - {file = "Pillow-8.2.0-pp37-pypy37_pp73-manylinux2010_x86_64.whl", hash = "sha256:22fd0f42ad15dfdde6c581347eaa4adb9a6fc4b865f90b23378aa7914895e120"}, - {file = "Pillow-8.2.0-pp37-pypy37_pp73-win32.whl", hash = "sha256:e98eca29a05913e82177b3ba3d198b1728e164869c613d76d0de4bde6768a50e"}, - {file = "Pillow-8.2.0-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:8b56553c0345ad6dcb2e9b433ae47d67f95fc23fe28a0bde15a120f25257e291"}, - {file = "Pillow-8.2.0.tar.gz", hash = "sha256:a787ab10d7bb5494e5f76536ac460741788f1fbce851068d73a87ca7c35fc3e1"}, -] -protobuf = [ - {file = "protobuf-3.17.3-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:ab6bb0e270c6c58e7ff4345b3a803cc59dbee19ddf77a4719c5b635f1d547aa8"}, - {file = "protobuf-3.17.3-cp27-cp27mu-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:13ee7be3c2d9a5d2b42a1030976f760f28755fcf5863c55b1460fd205e6cd637"}, - {file = "protobuf-3.17.3-cp35-cp35m-macosx_10_9_intel.whl", hash = "sha256:1556a1049ccec58c7855a78d27e5c6e70e95103b32de9142bae0576e9200a1b0"}, - {file = "protobuf-3.17.3-cp35-cp35m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:f0e59430ee953184a703a324b8ec52f571c6c4259d496a19d1cabcdc19dabc62"}, - {file = "protobuf-3.17.3-cp35-cp35m-win32.whl", hash = "sha256:a981222367fb4210a10a929ad5983ae93bd5a050a0824fc35d6371c07b78caf6"}, - {file = "protobuf-3.17.3-cp35-cp35m-win_amd64.whl", hash = "sha256:6d847c59963c03fd7a0cd7c488cadfa10cda4fff34d8bc8cba92935a91b7a037"}, - {file = "protobuf-3.17.3-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:145ce0af55c4259ca74993ddab3479c78af064002ec8227beb3d944405123c71"}, - {file = "protobuf-3.17.3-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:6ce4d8bf0321e7b2d4395e253f8002a1a5ffbcfd7bcc0a6ba46712c07d47d0b4"}, - {file = "protobuf-3.17.3-cp36-cp36m-win32.whl", hash = "sha256:7a4c97961e9e5b03a56f9a6c82742ed55375c4a25f2692b625d4087d02ed31b9"}, - {file = "protobuf-3.17.3-cp36-cp36m-win_amd64.whl", hash = "sha256:a22b3a0dbac6544dacbafd4c5f6a29e389a50e3b193e2c70dae6bbf7930f651d"}, - {file = "protobuf-3.17.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:ffea251f5cd3c0b9b43c7a7a912777e0bc86263436a87c2555242a348817221b"}, - {file = "protobuf-3.17.3-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:9b7a5c1022e0fa0dbde7fd03682d07d14624ad870ae52054849d8960f04bc764"}, - {file = "protobuf-3.17.3-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:8727ee027157516e2c311f218ebf2260a18088ffb2d29473e82add217d196b1c"}, - {file = "protobuf-3.17.3-cp37-cp37m-win32.whl", hash = "sha256:14c1c9377a7ffbeaccd4722ab0aa900091f52b516ad89c4b0c3bb0a4af903ba5"}, - {file = "protobuf-3.17.3-cp37-cp37m-win_amd64.whl", hash = "sha256:c56c050a947186ba51de4f94ab441d7f04fcd44c56df6e922369cc2e1a92d683"}, - {file = "protobuf-3.17.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:2ae692bb6d1992afb6b74348e7bb648a75bb0d3565a3f5eea5bec8f62bd06d87"}, - {file = "protobuf-3.17.3-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:99938f2a2d7ca6563c0ade0c5ca8982264c484fdecf418bd68e880a7ab5730b1"}, - {file = "protobuf-3.17.3-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:6902a1e4b7a319ec611a7345ff81b6b004b36b0d2196ce7a748b3493da3d226d"}, - {file = "protobuf-3.17.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:4ffbd23640bb7403574f7aff8368e2aeb2ec9a5c6306580be48ac59a6bac8bde"}, - {file = "protobuf-3.17.3-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:26010f693b675ff5a1d0e1bdb17689b8b716a18709113288fead438703d45539"}, - {file = "protobuf-3.17.3-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:e76d9686e088fece2450dbc7ee905f9be904e427341d289acbe9ad00b78ebd47"}, - {file = "protobuf-3.17.3-py2.py3-none-any.whl", hash = "sha256:2bfb815216a9cd9faec52b16fd2bfa68437a44b67c56bee59bc3926522ecb04e"}, - {file = "protobuf-3.17.3.tar.gz", hash = "sha256:72804ea5eaa9c22a090d2803813e280fb273b62d5ae497aaf3553d141c4fdd7b"}, -] -pyasn1 = [ - {file = "pyasn1-0.4.8-py2.4.egg", hash = "sha256:fec3e9d8e36808a28efb59b489e4528c10ad0f480e57dcc32b4de5c9d8c9fdf3"}, - {file = "pyasn1-0.4.8-py2.5.egg", hash = "sha256:0458773cfe65b153891ac249bcf1b5f8f320b7c2ce462151f8fa74de8934becf"}, - {file = "pyasn1-0.4.8-py2.6.egg", hash = "sha256:5c9414dcfede6e441f7e8f81b43b34e834731003427e5b09e4e00e3172a10f00"}, - {file = "pyasn1-0.4.8-py2.7.egg", hash = "sha256:6e7545f1a61025a4e58bb336952c5061697da694db1cae97b116e9c46abcf7c8"}, - {file = "pyasn1-0.4.8-py2.py3-none-any.whl", hash = "sha256:39c7e2ec30515947ff4e87fb6f456dfc6e84857d34be479c9d4a4ba4bf46aa5d"}, - {file = "pyasn1-0.4.8-py3.1.egg", hash = "sha256:78fa6da68ed2727915c4767bb386ab32cdba863caa7dbe473eaae45f9959da86"}, - {file = "pyasn1-0.4.8-py3.2.egg", hash = "sha256:08c3c53b75eaa48d71cf8c710312316392ed40899cb34710d092e96745a358b7"}, - {file = "pyasn1-0.4.8-py3.3.egg", hash = "sha256:03840c999ba71680a131cfaee6fab142e1ed9bbd9c693e285cc6aca0d555e576"}, - {file = "pyasn1-0.4.8-py3.4.egg", hash = "sha256:7ab8a544af125fb704feadb008c99a88805126fb525280b2270bb25cc1d78a12"}, - {file = "pyasn1-0.4.8-py3.5.egg", hash = "sha256:e89bf84b5437b532b0803ba5c9a5e054d21fec423a89952a74f87fa2c9b7bce2"}, - {file = "pyasn1-0.4.8-py3.6.egg", hash = "sha256:014c0e9976956a08139dc0712ae195324a75e142284d5f87f1a87ee1b068a359"}, - {file = "pyasn1-0.4.8-py3.7.egg", hash = "sha256:99fcc3c8d804d1bc6d9a099921e39d827026409a58f2a720dcdb89374ea0c776"}, - {file = "pyasn1-0.4.8.tar.gz", hash = "sha256:aef77c9fb94a3ac588e87841208bdec464471d9871bd5050a287cc9a475cd0ba"}, -] -pyasn1-modules = [ - {file = "pyasn1-modules-0.2.8.tar.gz", hash = "sha256:905f84c712230b2c592c19470d3ca8d552de726050d1d1716282a1f6146be65e"}, - {file = "pyasn1_modules-0.2.8-py2.4.egg", hash = "sha256:0fe1b68d1e486a1ed5473f1302bd991c1611d319bba158e98b106ff86e1d7199"}, - {file = "pyasn1_modules-0.2.8-py2.5.egg", hash = "sha256:fe0644d9ab041506b62782e92b06b8c68cca799e1a9636ec398675459e031405"}, - {file = "pyasn1_modules-0.2.8-py2.6.egg", hash = "sha256:a99324196732f53093a84c4369c996713eb8c89d360a496b599fb1a9c47fc3eb"}, - {file = "pyasn1_modules-0.2.8-py2.7.egg", hash = "sha256:0845a5582f6a02bb3e1bde9ecfc4bfcae6ec3210dd270522fee602365430c3f8"}, - {file = "pyasn1_modules-0.2.8-py2.py3-none-any.whl", hash = "sha256:a50b808ffeb97cb3601dd25981f6b016cbb3d31fbf57a8b8a87428e6158d0c74"}, - {file = "pyasn1_modules-0.2.8-py3.1.egg", hash = "sha256:f39edd8c4ecaa4556e989147ebf219227e2cd2e8a43c7e7fcb1f1c18c5fd6a3d"}, - {file = "pyasn1_modules-0.2.8-py3.2.egg", hash = "sha256:b80486a6c77252ea3a3e9b1e360bc9cf28eaac41263d173c032581ad2f20fe45"}, - {file = "pyasn1_modules-0.2.8-py3.3.egg", hash = "sha256:65cebbaffc913f4fe9e4808735c95ea22d7a7775646ab690518c056784bc21b4"}, - {file = "pyasn1_modules-0.2.8-py3.4.egg", hash = "sha256:15b7c67fabc7fc240d87fb9aabf999cf82311a6d6fb2c70d00d3d0604878c811"}, - {file = "pyasn1_modules-0.2.8-py3.5.egg", hash = "sha256:426edb7a5e8879f1ec54a1864f16b882c2837bfd06eee62f2c982315ee2473ed"}, - {file = "pyasn1_modules-0.2.8-py3.6.egg", hash = "sha256:cbac4bc38d117f2a49aeedec4407d23e8866ea4ac27ff2cf7fb3e5b570df19e0"}, - {file = "pyasn1_modules-0.2.8-py3.7.egg", hash = "sha256:c29a5e5cc7a3f05926aff34e097e84f8589cd790ce0ed41b67aed6857b26aafd"}, -] pycodestyle = [ {file = "pycodestyle-2.6.0-py2.py3-none-any.whl", hash = "sha256:2295e7b2f6b5bd100585ebcb1f616591b652db8a741695b3d8f5d28bdc934367"}, {file = "pycodestyle-2.6.0.tar.gz", hash = "sha256:c58a7d2815e0e8d7972bf1803331fb0152f867bd89adf8a01dfd55085434192e"}, @@ -1361,44 +473,32 @@ pyflakes = [ pygraphviz = [ {file = "pygraphviz-1.6.zip", hash = "sha256:411ae84a5bc313e3e1523a1cace59159f512336318a510573b47f824edef8860"}, ] -pymint = [] -pyparsing = [ - {file = "pyparsing-2.4.7-py2.py3-none-any.whl", hash = "sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b"}, - {file = "pyparsing-2.4.7.tar.gz", hash = "sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1"}, +pymint = [ + {file = "pymint-0.2.11-py3-none-any.whl", hash = "sha256:63b0b0ffda571fd434f29a11e8c7afa711fdc4fe92177262bfc79bd5d113fa42"}, + {file = "pymint-0.2.11.tar.gz", hash = "sha256:7301b75556b24b47622419e23619ce0b5a3f71bfd8e9f24807c91d5470043d0e"}, ] pyrsistent = [ - {file = "pyrsistent-0.17.3.tar.gz", hash = "sha256:2e636185d9eb976a18a8a8e96efce62f2905fea90041958d8cc2a189756ebf3e"}, -] -python-dateutil = [ - {file = "python-dateutil-2.8.1.tar.gz", hash = "sha256:73ebfe9dbf22e832286dafa60473e4cd239f8592f699aa5adaf10050e6e1823c"}, - {file = "python_dateutil-2.8.1-py2.py3-none-any.whl", hash = "sha256:75bb3f31ea686f1197762692a9ee6a7550b59fc6ca3a1f4b5d7e32fb98e2da2a"}, -] -pytz = [ - {file = "pytz-2021.1-py2.py3-none-any.whl", hash = "sha256:eb10ce3e7736052ed3623d49975ce333bcd712c7bb19a58b9e2089d4057d0798"}, - {file = "pytz-2021.1.tar.gz", hash = "sha256:83a4a90894bf38e243cf052c8b58f381bfe9a7a483f6a9cab140bc7f702ac4da"}, -] -pyyaml = [ - {file = "PyYAML-5.4.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:3b2b1824fe7112845700f815ff6a489360226a5609b96ec2190a45e62a9fc922"}, - {file = "PyYAML-5.4.1-cp27-cp27m-win32.whl", hash = "sha256:129def1b7c1bf22faffd67b8f3724645203b79d8f4cc81f674654d9902cb4393"}, - {file = "PyYAML-5.4.1-cp27-cp27m-win_amd64.whl", hash = "sha256:4465124ef1b18d9ace298060f4eccc64b0850899ac4ac53294547536533800c8"}, - {file = "PyYAML-5.4.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:bb4191dfc9306777bc594117aee052446b3fa88737cd13b7188d0e7aa8162185"}, - {file = "PyYAML-5.4.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:6c78645d400265a062508ae399b60b8c167bf003db364ecb26dcab2bda048253"}, - {file = "PyYAML-5.4.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:4e0583d24c881e14342eaf4ec5fbc97f934b999a6828693a99157fde912540cc"}, - {file = "PyYAML-5.4.1-cp36-cp36m-win32.whl", hash = "sha256:3bd0e463264cf257d1ffd2e40223b197271046d09dadf73a0fe82b9c1fc385a5"}, - {file = "PyYAML-5.4.1-cp36-cp36m-win_amd64.whl", hash = "sha256:e4fac90784481d221a8e4b1162afa7c47ed953be40d31ab4629ae917510051df"}, - {file = "PyYAML-5.4.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:5accb17103e43963b80e6f837831f38d314a0495500067cb25afab2e8d7a4018"}, - {file = "PyYAML-5.4.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:e1d4970ea66be07ae37a3c2e48b5ec63f7ba6804bdddfdbd3cfd954d25a82e63"}, - {file = "PyYAML-5.4.1-cp37-cp37m-win32.whl", hash = "sha256:dd5de0646207f053eb0d6c74ae45ba98c3395a571a2891858e87df7c9b9bd51b"}, - {file = "PyYAML-5.4.1-cp37-cp37m-win_amd64.whl", hash = "sha256:08682f6b72c722394747bddaf0aa62277e02557c0fd1c42cb853016a38f8dedf"}, - {file = "PyYAML-5.4.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d2d9808ea7b4af864f35ea216be506ecec180628aced0704e34aca0b040ffe46"}, - {file = "PyYAML-5.4.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:8c1be557ee92a20f184922c7b6424e8ab6691788e6d86137c5d93c1a6ec1b8fb"}, - {file = "PyYAML-5.4.1-cp38-cp38-win32.whl", hash = "sha256:fa5ae20527d8e831e8230cbffd9f8fe952815b2b7dae6ffec25318803a7528fc"}, - {file = "PyYAML-5.4.1-cp38-cp38-win_amd64.whl", hash = "sha256:0f5f5786c0e09baddcd8b4b45f20a7b5d61a7e7e99846e3c799b05c7c53fa696"}, - {file = "PyYAML-5.4.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:294db365efa064d00b8d1ef65d8ea2c3426ac366c0c4368d930bf1c5fb497f77"}, - {file = "PyYAML-5.4.1-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:74c1485f7707cf707a7aef42ef6322b8f97921bd89be2ab6317fd782c2d53183"}, - {file = "PyYAML-5.4.1-cp39-cp39-win32.whl", hash = "sha256:49d4cdd9065b9b6e206d0595fee27a96b5dd22618e7520c33204a4a3239d5b10"}, - {file = "PyYAML-5.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:c20cfa2d49991c8b4147af39859b167664f2ad4561704ee74c1de03318e898db"}, - {file = "PyYAML-5.4.1.tar.gz", hash = "sha256:607774cbba28732bfa802b54baa7484215f530991055bb562efbed5b2f20a45e"}, + {file = "pyrsistent-0.18.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:f4c8cabb46ff8e5d61f56a037974228e978f26bfefce4f61a4b1ac0ba7a2ab72"}, + {file = "pyrsistent-0.18.0-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:da6e5e818d18459fa46fac0a4a4e543507fe1110e808101277c5a2b5bab0cd2d"}, + {file = "pyrsistent-0.18.0-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:5e4395bbf841693eaebaa5bb5c8f5cdbb1d139e07c975c682ec4e4f8126e03d2"}, + {file = "pyrsistent-0.18.0-cp36-cp36m-win32.whl", hash = "sha256:527be2bfa8dc80f6f8ddd65242ba476a6c4fb4e3aedbf281dfbac1b1ed4165b1"}, + {file = "pyrsistent-0.18.0-cp36-cp36m-win_amd64.whl", hash = "sha256:2aaf19dc8ce517a8653746d98e962ef480ff34b6bc563fc067be6401ffb457c7"}, + {file = "pyrsistent-0.18.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:58a70d93fb79dc585b21f9d72487b929a6fe58da0754fa4cb9f279bb92369396"}, + {file = "pyrsistent-0.18.0-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:4916c10896721e472ee12c95cdc2891ce5890898d2f9907b1b4ae0f53588b710"}, + {file = "pyrsistent-0.18.0-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:73ff61b1411e3fb0ba144b8f08d6749749775fe89688093e1efef9839d2dcc35"}, + {file = "pyrsistent-0.18.0-cp37-cp37m-win32.whl", hash = "sha256:b29b869cf58412ca5738d23691e96d8aff535e17390128a1a52717c9a109da4f"}, + {file = "pyrsistent-0.18.0-cp37-cp37m-win_amd64.whl", hash = "sha256:097b96f129dd36a8c9e33594e7ebb151b1515eb52cceb08474c10a5479e799f2"}, + {file = "pyrsistent-0.18.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:772e94c2c6864f2cd2ffbe58bb3bdefbe2a32afa0acb1a77e472aac831f83427"}, + {file = "pyrsistent-0.18.0-cp38-cp38-manylinux1_i686.whl", hash = "sha256:c1a9ff320fa699337e05edcaae79ef8c2880b52720bc031b219e5b5008ebbdef"}, + {file = "pyrsistent-0.18.0-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:cd3caef37a415fd0dae6148a1b6957a8c5f275a62cca02e18474608cb263640c"}, + {file = "pyrsistent-0.18.0-cp38-cp38-win32.whl", hash = "sha256:e79d94ca58fcafef6395f6352383fa1a76922268fa02caa2272fff501c2fdc78"}, + {file = "pyrsistent-0.18.0-cp38-cp38-win_amd64.whl", hash = "sha256:a0c772d791c38bbc77be659af29bb14c38ced151433592e326361610250c605b"}, + {file = "pyrsistent-0.18.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:d5ec194c9c573aafaceebf05fc400656722793dac57f254cd4741f3c27ae57b4"}, + {file = "pyrsistent-0.18.0-cp39-cp39-manylinux1_i686.whl", hash = "sha256:6b5eed00e597b5b5773b4ca30bd48a5774ef1e96f2a45d105db5b4ebb4bca680"}, + {file = "pyrsistent-0.18.0-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:48578680353f41dca1ca3dc48629fb77dfc745128b56fc01096b2530c13fd426"}, + {file = "pyrsistent-0.18.0-cp39-cp39-win32.whl", hash = "sha256:f3ef98d7b76da5eb19c37fda834d50262ff9167c65658d1d8f974d2e4d90676b"}, + {file = "pyrsistent-0.18.0-cp39-cp39-win_amd64.whl", hash = "sha256:404e1f1d254d314d55adb8d87f4f465c8693d6f902f67eb6ef5b4526dc58e6ea"}, + {file = "pyrsistent-0.18.0.tar.gz", hash = "sha256:773c781216f8c2900b42a7b638d5b517bb134ae1acbebe4d1e8f1f41ea60eb4b"}, ] regex = [ {file = "regex-2020.11.11-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:dd7bee615680d940dd44ac0a479f2bc5f73d6ca63a5915cd8d30739c14ca522c"}, @@ -1443,119 +543,21 @@ regex = [ {file = "regex-2020.11.11-cp39-cp39-win_amd64.whl", hash = "sha256:bf02ab95ff5261ba108725dbd795bf6395eaac1b8468b41472d82d35b12b0295"}, {file = "regex-2020.11.11.tar.gz", hash = "sha256:0a235841237d4487329bcabcb5b902858f7967f5e684e08e968367f25b2c3d37"}, ] -requests = [ - {file = "requests-2.25.1-py2.py3-none-any.whl", hash = "sha256:c210084e36a42ae6b9219e00e48287def368a26d03a048ddad7bfee44f75871e"}, - {file = "requests-2.25.1.tar.gz", hash = "sha256:27973dd4a904a4f13b263a19c866c13b92a39ed1c964655f025f3f8d3d75b804"}, -] -requests-oauthlib = [ - {file = "requests-oauthlib-1.3.0.tar.gz", hash = "sha256:b4261601a71fd721a8bd6d7aa1cc1d6a8a93b4a9f5e96626f8e4d91e8beeaa6a"}, - {file = "requests_oauthlib-1.3.0-py2.py3-none-any.whl", hash = "sha256:7f71572defaecd16372f9006f33c2ec8c077c3cfa6f5911a9a90202beb513f3d"}, - {file = "requests_oauthlib-1.3.0-py3.7.egg", hash = "sha256:fa6c47b933f01060936d87ae9327fead68768b69c6c9ea2109c48be30f2d4dbc"}, -] rope = [ {file = "rope-0.18.0.tar.gz", hash = "sha256:786b5c38c530d4846aa68a42604f61b4e69a493390e3ca11b88df0fbfdc3ed04"}, ] -rsa = [ - {file = "rsa-4.7.2-py3-none-any.whl", hash = "sha256:78f9a9bf4e7be0c5ded4583326e7461e3a3c5aae24073648b4bdfa797d78c9d2"}, - {file = "rsa-4.7.2.tar.gz", hash = "sha256:9d689e6ca1b3038bc82bf8d23e944b6b6037bc02301a574935b2dd946e0353b9"}, -] -salib = [ - {file = "SALib-1.3.13.tar.gz", hash = "sha256:509c149c81d9cbf7e54ab275769bd855fb0aa5a699570c76f2ffa183a9c4041a"}, -] -scikit-learn = [ - {file = "scikit-learn-0.23.2.tar.gz", hash = "sha256:20766f515e6cd6f954554387dfae705d93c7b544ec0e6c6a5d8e006f6f7ef480"}, - {file = "scikit_learn-0.23.2-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:98508723f44c61896a4e15894b2016762a55555fbf09365a0bb1870ecbd442de"}, - {file = "scikit_learn-0.23.2-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:a64817b050efd50f9abcfd311870073e500ae11b299683a519fbb52d85e08d25"}, - {file = "scikit_learn-0.23.2-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:daf276c465c38ef736a79bd79fc80a249f746bcbcae50c40945428f7ece074f8"}, - {file = "scikit_learn-0.23.2-cp36-cp36m-win32.whl", hash = "sha256:cb3e76380312e1f86abd20340ab1d5b3cc46a26f6593d3c33c9ea3e4c7134028"}, - {file = "scikit_learn-0.23.2-cp36-cp36m-win_amd64.whl", hash = "sha256:0a127cc70990d4c15b1019680bfedc7fec6c23d14d3719fdf9b64b22d37cdeca"}, - {file = "scikit_learn-0.23.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:2aa95c2f17d2f80534156215c87bee72b6aa314a7f8b8fe92a2d71f47280570d"}, - {file = "scikit_learn-0.23.2-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:6c28a1d00aae7c3c9568f61aafeaad813f0f01c729bee4fd9479e2132b215c1d"}, - {file = "scikit_learn-0.23.2-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:da8e7c302003dd765d92a5616678e591f347460ac7b53e53d667be7dfe6d1b10"}, - {file = "scikit_learn-0.23.2-cp37-cp37m-win32.whl", hash = "sha256:d9a1ce5f099f29c7c33181cc4386660e0ba891b21a60dc036bf369e3a3ee3aec"}, - {file = "scikit_learn-0.23.2-cp37-cp37m-win_amd64.whl", hash = "sha256:914ac2b45a058d3f1338d7736200f7f3b094857758895f8667be8a81ff443b5b"}, - {file = "scikit_learn-0.23.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:7671bbeddd7f4f9a6968f3b5442dac5f22bf1ba06709ef888cc9132ad354a9ab"}, - {file = "scikit_learn-0.23.2-cp38-cp38-manylinux1_i686.whl", hash = "sha256:d0dcaa54263307075cb93d0bee3ceb02821093b1b3d25f66021987d305d01dce"}, - {file = "scikit_learn-0.23.2-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:5ce7a8021c9defc2b75620571b350acc4a7d9763c25b7593621ef50f3bd019a2"}, - {file = "scikit_learn-0.23.2-cp38-cp38-win32.whl", hash = "sha256:0d39748e7c9669ba648acf40fb3ce96b8a07b240db6888563a7cb76e05e0d9cc"}, - {file = "scikit_learn-0.23.2-cp38-cp38-win_amd64.whl", hash = "sha256:1b8a391de95f6285a2f9adffb7db0892718950954b7149a70c783dc848f104ea"}, -] -scipy = [ - {file = "scipy-1.6.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:a15a1f3fc0abff33e792d6049161b7795909b40b97c6cc2934ed54384017ab76"}, - {file = "scipy-1.6.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:e79570979ccdc3d165456dd62041d9556fb9733b86b4b6d818af7a0afc15f092"}, - {file = "scipy-1.6.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:a423533c55fec61456dedee7b6ee7dce0bb6bfa395424ea374d25afa262be261"}, - {file = "scipy-1.6.1-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:33d6b7df40d197bdd3049d64e8e680227151673465e5d85723b3b8f6b15a6ced"}, - {file = "scipy-1.6.1-cp37-cp37m-win32.whl", hash = "sha256:6725e3fbb47da428794f243864f2297462e9ee448297c93ed1dcbc44335feb78"}, - {file = "scipy-1.6.1-cp37-cp37m-win_amd64.whl", hash = "sha256:5fa9c6530b1661f1370bcd332a1e62ca7881785cc0f80c0d559b636567fab63c"}, - {file = "scipy-1.6.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:bd50daf727f7c195e26f27467c85ce653d41df4358a25b32434a50d8870fc519"}, - {file = "scipy-1.6.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:f46dd15335e8a320b0fb4685f58b7471702234cba8bb3442b69a3e1dc329c345"}, - {file = "scipy-1.6.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:0e5b0ccf63155d90da576edd2768b66fb276446c371b73841e3503be1d63fb5d"}, - {file = "scipy-1.6.1-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:2481efbb3740977e3c831edfd0bd9867be26387cacf24eb5e366a6a374d3d00d"}, - {file = "scipy-1.6.1-cp38-cp38-win32.whl", hash = "sha256:68cb4c424112cd4be886b4d979c5497fba190714085f46b8ae67a5e4416c32b4"}, - {file = "scipy-1.6.1-cp38-cp38-win_amd64.whl", hash = "sha256:5f331eeed0297232d2e6eea51b54e8278ed8bb10b099f69c44e2558c090d06bf"}, - {file = "scipy-1.6.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:0c8a51d33556bf70367452d4d601d1742c0e806cd0194785914daf19775f0e67"}, - {file = "scipy-1.6.1-cp39-cp39-manylinux1_i686.whl", hash = "sha256:83bf7c16245c15bc58ee76c5418e46ea1811edcc2e2b03041b804e46084ab627"}, - {file = "scipy-1.6.1-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:794e768cc5f779736593046c9714e0f3a5940bc6dcc1dba885ad64cbfb28e9f0"}, - {file = "scipy-1.6.1-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:5da5471aed911fe7e52b86bf9ea32fb55ae93e2f0fac66c32e58897cfb02fa07"}, - {file = "scipy-1.6.1-cp39-cp39-win32.whl", hash = "sha256:8e403a337749ed40af60e537cc4d4c03febddcc56cd26e774c9b1b600a70d3e4"}, - {file = "scipy-1.6.1-cp39-cp39-win_amd64.whl", hash = "sha256:a5193a098ae9f29af283dcf0041f762601faf2e595c0db1da929875b7570353f"}, - {file = "scipy-1.6.1.tar.gz", hash = "sha256:c4fceb864890b6168e79b0e714c585dbe2fd4222768ee90bc1aa0f8218691b11"}, -] -seaborn = [ - {file = "seaborn-0.11.1-py3-none-any.whl", hash = "sha256:4e1cce9489449a1c6ff3c567f2113cdb41122f727e27a984950d004a88ef3c5c"}, - {file = "seaborn-0.11.1.tar.gz", hash = "sha256:44e78eaed937c5a87fc7a892c329a7cc091060b67ebd1d0d306b446a74ba01ad"}, -] six = [ - {file = "six-1.15.0-py2.py3-none-any.whl", hash = "sha256:8b74bedcbbbaca38ff6d7491d76f2b06b3592611af620f8426e82dddb04a5ced"}, - {file = "six-1.15.0.tar.gz", hash = "sha256:30639c035cdb23534cd4aa2dd52c3bf48f06e5f4a941509c8bafd8ce11080259"}, + {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, + {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, ] tabulate = [ {file = "tabulate-0.8.9-py3-none-any.whl", hash = "sha256:d7c013fe7abbc5e491394e10fa845f8f32fe54f8dc60c6622c6cf482d25d47e4"}, {file = "tabulate-0.8.9.tar.gz", hash = "sha256:eb1d13f25760052e8931f2ef80aaf6045a6cceb47514db8beab24cded16f13a7"}, ] -tensorboard = [ - {file = "tensorboard-2.5.0-py3-none-any.whl", hash = "sha256:e167460085b6528956b33bab1c970c989cdce47a6616273880733f5e7bde452e"}, -] -tensorboard-data-server = [ - {file = "tensorboard_data_server-0.6.1-py3-none-any.whl", hash = "sha256:809fe9887682d35c1f7d1f54f0f40f98bb1f771b14265b453ca051e2ce58fca7"}, - {file = "tensorboard_data_server-0.6.1-py3-none-macosx_10_9_x86_64.whl", hash = "sha256:fa8cef9be4fcae2f2363c88176638baf2da19c5ec90addb49b1cde05c95c88ee"}, - {file = "tensorboard_data_server-0.6.1-py3-none-manylinux2010_x86_64.whl", hash = "sha256:d8237580755e58eff68d1f3abefb5b1e39ae5c8b127cc40920f9c4fb33f4b98a"}, -] -tensorboard-plugin-wit = [ - {file = "tensorboard_plugin_wit-1.8.0-py3-none-any.whl", hash = "sha256:2a80d1c551d741e99b2f197bb915d8a133e24adb8da1732b840041860f91183a"}, -] -tensorflow = [ - {file = "tensorflow-2.5.0-cp36-cp36m-macosx_10_11_x86_64.whl", hash = "sha256:7e1351ce05b897d5cf1042066b6929ca3f595a717849421ae92dbe8d6d2f1c74"}, - {file = "tensorflow-2.5.0-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:31a3ea994c336fc5a6ba0e6d61f131262b2c6dbff97e2b7473ff6da0cf9383f7"}, - {file = "tensorflow-2.5.0-cp36-cp36m-win_amd64.whl", hash = "sha256:c45059b42bca01ce441004abb965acf7838b40d12e036920063bd7ac540def9a"}, - {file = "tensorflow-2.5.0-cp37-cp37m-macosx_10_11_x86_64.whl", hash = "sha256:616bc8094cb289b3bd21eded2196b0dba65bce53bad112efcaf2acb6f7d9e6a5"}, - {file = "tensorflow-2.5.0-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:739d25273ccc10fedc74517de099bd5b16a274d1295fad6bfef834ad28cc3401"}, - {file = "tensorflow-2.5.0-cp37-cp37m-win_amd64.whl", hash = "sha256:68b70ca7df7f5f8fbe3d7240e937b3ea8b1a25e51710f60293e7edada00257a2"}, - {file = "tensorflow-2.5.0-cp38-cp38-macosx_10_11_x86_64.whl", hash = "sha256:c46b1d1b0eec54577d7ba545e3951c9dd0355ca05a8eb776c95d9a3e22e7be9c"}, - {file = "tensorflow-2.5.0-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:34ab87aac9093de98cbba68d7e8dca9159c36acd06a03e5749c956c7ab08d9da"}, - {file = "tensorflow-2.5.0-cp38-cp38-win_amd64.whl", hash = "sha256:46f10a2edc694bb54a2d869a65b5a09705dab1874a89b529990a943416ad48aa"}, - {file = "tensorflow-2.5.0-cp39-cp39-macosx_10_11_x86_64.whl", hash = "sha256:baebb9c95ef1815bb410317ad525dd3dbb26064fe95636b51486459b6536bc6e"}, - {file = "tensorflow-2.5.0-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:1ea003f9e11508d0336c242a2a3bc73aea205dd5b31892c3e1d7f5d0f0e60c0a"}, - {file = "tensorflow-2.5.0-cp39-cp39-win_amd64.whl", hash = "sha256:4edec9b9f6ef8f1407762a3a6bd050173177f686d5ea6b59e91487b645173f73"}, -] -tensorflow-estimator = [ - {file = "tensorflow_estimator-2.5.0-py2.py3-none-any.whl", hash = "sha256:d1fe76dee8b1dcab865d807a0246da0a9c4a635b1eba6e9545bf216c3aad6955"}, -] -termcolor = [ - {file = "termcolor-1.1.0.tar.gz", hash = "sha256:1d6d69ce66211143803fbc56652b41d73b4a400a2891d7bf7a1cdf4c02de613b"}, -] -threadpoolctl = [ - {file = "threadpoolctl-2.1.0-py3-none-any.whl", hash = "sha256:38b74ca20ff3bb42caca8b00055111d74159ee95c4370882bbff2b93d24da725"}, - {file = "threadpoolctl-2.1.0.tar.gz", hash = "sha256:ddc57c96a38beb63db45d6c159b5ab07b6bced12c45a1f07b2b92f272aebfa6b"}, -] toml = [ {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"}, {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"}, ] -tqdm = [ - {file = "tqdm-4.61.1-py2.py3-none-any.whl", hash = "sha256:aa0c29f03f298951ac6318f7c8ce584e48fa22ec26396e6411e43d038243bdb2"}, - {file = "tqdm-4.61.1.tar.gz", hash = "sha256:24be966933e942be5f074c29755a95b315c69a91f839a29139bf26ffffe2d3fd"}, -] typed-ast = [ {file = "typed_ast-1.4.1-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:73d785a950fc82dd2a25897d525d003f6378d1cb23ab305578394694202a58c3"}, {file = "typed_ast-1.4.1-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:aaee9905aee35ba5905cfb3c62f3e83b3bec7b39413f0a7f19be4e547ea01ebb"}, @@ -1584,14 +586,3 @@ typing-extensions = [ {file = "typing_extensions-3.7.4.3-py3-none-any.whl", hash = "sha256:7cb407020f00f7bfc3cb3e7881628838e69d8f3fcab2f64742a5e76b2f841918"}, {file = "typing_extensions-3.7.4.3.tar.gz", hash = "sha256:99d4073b617d30288f569d3f13d2bd7548c3a7e4c8de87db09a9d29bb3a4a60c"}, ] -urllib3 = [ - {file = "urllib3-1.26.5-py2.py3-none-any.whl", hash = "sha256:753a0374df26658f99d826cfe40394a686d05985786d946fbe4165b5148f5a7c"}, - {file = "urllib3-1.26.5.tar.gz", hash = "sha256:a7acd0977125325f516bda9735fa7142b909a8d01e8b2e4c8108d0984e6e0098"}, -] -werkzeug = [ - {file = "Werkzeug-2.0.1-py3-none-any.whl", hash = "sha256:6c1ec500dcdba0baa27600f6a22f6333d8b662d22027ff9f6202e3367413caa8"}, - {file = "Werkzeug-2.0.1.tar.gz", hash = "sha256:1de1db30d010ff1af14a009224ec49ab2329ad2cde454c8a708130642d579c42"}, -] -wrapt = [ - {file = "wrapt-1.12.1.tar.gz", hash = "sha256:b62ffa81fb85f4332a4f609cab4ac40709470da05643a082ec1eb88e6d9b97d7"}, -] diff --git a/pyproject.toml b/pyproject.toml index 96639d5..01c76eb 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -23,6 +23,8 @@ dafd = "latest" numpy = "^1.19.4" tabulate = "^0.8.7" pymint = "^0.2.11" +art = "^5.2" +jsonschema = "^3.2.0" [tool.poetry.dev-dependencies] mypy = "^0.782" From 1cf177b1b0d7176f0f716a5387ecc45a63721b14 Mon Sep 17 00:00:00 2001 From: Radhakrishna Sanka Date: Fri, 16 Jul 2021 14:51:10 -0400 Subject: [PATCH 30/36] Some more error throwing for fluid number interactions. --- lfr/compiler/module.py | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/lfr/compiler/module.py b/lfr/compiler/module.py index a186668..a7d587f 100644 --- a/lfr/compiler/module.py +++ b/lfr/compiler/module.py @@ -1,7 +1,7 @@ from __future__ import annotations import copy -from typing import Dict, List, Optional +from typing import Dict, List, Optional, Union from lfr.compiler.moduleio import ModuleIO from lfr.fig.fignode import FIGNode, Flow, IONode, IOType from lfr.fig.fluidinteractiongraph import FluidInteractionGraph @@ -158,7 +158,7 @@ def add_interaction_output(self, output: Flow, interaction: Interaction): def add_fluid_numeric_interaction( self, fluid1: Flow, - number: float, + number: Union[int, float], interaction_type: InteractionType, ) -> Interaction: # finteraction = FluidInteraction(fluid1=fluid1, interactiontype=interaction) @@ -167,10 +167,19 @@ def add_fluid_numeric_interaction( if interaction_type is InteractionType.METER: finteraction = FluidNumberInteraction(fluid1, number, interaction_type) elif interaction_type is InteractionType.DILUTE: - finteraction = FluidNumberInteraction(fluid1, number, interaction_type) + if isinstance(number, float): + finteraction = FluidNumberInteraction(fluid1, number, interaction_type) + else: + # If its a variable get the corresponding value for it + # from the variable store + raise NotImplementedError() elif interaction_type is InteractionType.DIVIDE: - assert isinstance(number, int) - finteraction = FluidIntegerInteraction(fluid1, number, interaction_type) + if isinstance(number, int): + finteraction = FluidIntegerInteraction(fluid1, number, interaction_type) + else: + # If its a variable get the corresponding value for it + # from the variable store + raise NotImplementedError() else: raise Exception("Unsupported Numeric Operator") From caab50bb43410ab7da54f29c0c4e139191090a49 Mon Sep 17 00:00:00 2001 From: Radhakrishna Sanka Date: Fri, 16 Jul 2021 14:53:16 -0400 Subject: [PATCH 31/36] Updated docs --- nodes_todo.md | 5 +++++ notes.md => notes_matchstrings.md | 0 2 files changed, 5 insertions(+) create mode 100644 nodes_todo.md rename notes.md => notes_matchstrings.md (100%) diff --git a/nodes_todo.md b/nodes_todo.md new file mode 100644 index 0000000..81817cc --- /dev/null +++ b/nodes_todo.md @@ -0,0 +1,5 @@ +TODO - Need to revisit how the datavaules are getting passed around and if the +FluidicInteraction creation funcitons should take in vectors as values +(I think not...). + +Working on improving the logic expression processing and whatnot should help the situation \ No newline at end of file diff --git a/notes.md b/notes_matchstrings.md similarity index 100% rename from notes.md rename to notes_matchstrings.md From d1ecb4d76b5a6a2d90ecd9dbc8dd3977f832fa3b Mon Sep 17 00:00:00 2001 From: Radhakrishna Sanka Date: Wed, 21 Jul 2021 09:47:23 -0400 Subject: [PATCH 32/36] - Fixed issue where nodefilter was not being initialized properly - Added some scaffholding code for running mux_02_in with the debugger --- .vscode/launch.json | 2 + lfr/cmdline.py | 4 +- lfr/graphmatch/interface.py | 57 +++++++--- lfr/graphmatch/matchpatterngenerator.py | 18 +++- lfr/graphmatch/nodefilter.py | 1 + lfr/netlistgenerator/generator.py | 134 ++++++++++++++++++++++++ lfr/netlistgenerator/mappinglibrary.py | 3 + poetry.lock | 14 ++- pyeda_test.py | 13 +++ pyproject.toml | 1 + 10 files changed, 225 insertions(+), 22 deletions(-) create mode 100644 pyeda_test.py diff --git a/.vscode/launch.json b/.vscode/launch.json index 9a9d34f..abf2e31 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -74,6 +74,8 @@ "args": [ "--outpath", "./out/", + "--technology", + "mlsi", "${file}" ], "console": "integratedTerminal" diff --git a/lfr/cmdline.py b/lfr/cmdline.py index 5f9954e..41cc6f0 100644 --- a/lfr/cmdline.py +++ b/lfr/cmdline.py @@ -17,6 +17,7 @@ generate, generate_dropx_library, generate_mars_library, + generate_mlsi_library, ) from lfr.utils import print_netlist, printgraph, serialize_netlist from lfr.preprocessor import PreProcessor @@ -150,8 +151,7 @@ def main(): elif args.technology == "mars": library = generate_mars_library() elif args.technology == "mlsi": - print("Implement Library for MLSI") - pass + library = generate_mlsi_library() else: print("Implement Library for whatever else") pass diff --git a/lfr/graphmatch/interface.py b/lfr/graphmatch/interface.py index 07adc4d..5f74107 100644 --- a/lfr/graphmatch/interface.py +++ b/lfr/graphmatch/interface.py @@ -18,23 +18,48 @@ def match_node_constraints( # Check if the 1) Constraint type matches, 2) if the same nodes in the GM.mapping # match the ones denoted by the subgraph LFR + # Generate new IDs for each of the nodes from the fig and the netlist matches in + # the mapping + # Loop through each of the nodes in the fig and the mapping + new_ids = {} + new_id_counter = 0 + for key, value in mapping.items(): + new_ids[key] = str(new_id_counter) + new_ids[value] = str(new_id_counter) + new_id_counter += 1 + # Extract the subgraph pairings in the to know what fig node matches what matchnode - pattern_graph_coloring_map = dict() - pattern_graph_node_filters = dict() - - for fig_vertex_id, parttern_vertex_id in mapping.items(): - # Get Constraints for fig_vertex_id and the NodeFilter for the pattern_vertex_id - fig_node = fig.get_fignode(fig_vertex_id) - node_filter = semantic_information[parttern_vertex_id] - - # Load up all the constraints for this particular fignode - pattern_graph_coloring_map[fig_node] = node_filter.get_constriants() - pattern_graph_node_filters[fig_node] = node_filter - - # TODO - Now check to see if all the constraints are a match in node filter - for fig_node in pattern_graph_coloring_map.keys(): - # Get the constraints on the fig - fig_constraints = fig.get_fig_annotations(fig_node) + fig_distribution_set = {} + reggie_distribution_annotations = {} + + for fig_id, reggie_id in mapping.items(): + + new_id = new_ids[reggie_id] + node_filter = semantic_information[reggie_id] + + # Skip getting the constraints for the match nodes + if len(node_filter.get_constriants()) == 0: + continue + + for constriant in node_filter.get_constriants(): + if new_id not in reggie_distribution_annotations.keys(): + reggie_distribution_annotations[new_id] = {constriant[1]} + else: + reggie_distribution_annotations[new_id].add(constriant[1]) + + new_id = new_ids[fig_id] + + try: + annotations = fig.get_fig_annotations(fig_id) + except KeyError: + print("Missing annotation for fig node: {}".format(fig_id)) + continue + + for annotation in annotations: + if new_id not in fig_distribution_set.keys(): + fig_distribution_set[new_id] = {annotation.id} + else: + fig_distribution_set[new_id].add(annotation.id) return True diff --git a/lfr/graphmatch/matchpatterngenerator.py b/lfr/graphmatch/matchpatterngenerator.py index fe79100..b40710b 100644 --- a/lfr/graphmatch/matchpatterngenerator.py +++ b/lfr/graphmatch/matchpatterngenerator.py @@ -17,14 +17,22 @@ def enterVertex(self, ctx: reggieParser.VertexContext): self._vertices_stack.append(vertex_id) + # Skip the generation if the vertex is already in the structural template + if vertex_id in self.structural_template.nodes: + return + + # Get the type filters for the semantic template label_filters = [] if ctx.labelfilter() is not None: label_filters = [label.getText() for label in ctx.labelfilter().label()] - # TODO - Get the coloring filter + # Get the coloring filter coloring_labels = [] if ctx.coloringfilter() is not None: - coloring_labels = [s.getText() for s in ctx.coloringfilter().STRING()] + coloring_labels = [ + MatchPatternGenerator.remove_quotes(s.getText()) + for s in ctx.coloringfilter().STRING() + ] constraints_tuples = [] for i in range(0, len(coloring_labels), 2): constraints_tuples.append((coloring_labels[i], coloring_labels[i + 1])) @@ -38,7 +46,7 @@ def enterVertex(self, ctx: reggieParser.VertexContext): else: self.structural_template.add_node(vertex_id) self.semantic_template[vertex_id] = NodeFilter( - node_attributes_filter=label_filters, + node_types_filter=label_filters, node_constraints=constraints_tuples, ) @@ -55,3 +63,7 @@ def exitVertex2vertex(self, ctx: reggieParser.Vertex2vertexContext): end = self._vertices_stack[i] # Generate the graph self.structural_template.add_edge(start, end) + + @staticmethod + def remove_quotes(s: str) -> str: + return s.replace('"', "") diff --git a/lfr/graphmatch/nodefilter.py b/lfr/graphmatch/nodefilter.py index b2da47c..8376e3a 100644 --- a/lfr/graphmatch/nodefilter.py +++ b/lfr/graphmatch/nodefilter.py @@ -21,6 +21,7 @@ def is_valid_node_type(self, type_string: str) -> bool: def is_valid_attribute_type(self, attributestring: str) -> bool: # TODO - Figure out what this will be in tuture + raise NotImplementedError("Currently do not support node attributes") return True def get_constriants(self) -> List[Tuple[str, str]]: diff --git a/lfr/netlistgenerator/generator.py b/lfr/netlistgenerator/generator.py index 5260eff..60a82b0 100644 --- a/lfr/netlistgenerator/generator.py +++ b/lfr/netlistgenerator/generator.py @@ -48,6 +48,140 @@ # pass +def generate_mlsi_library() -> MappingLibrary: + + library = MappingLibrary("mlsi") + # PORT + port_inputs = [] + port_inputs.append(ConnectingOption(None, [None])) + port_inputs.append(ConnectingOption(None, [None])) + port_inputs.append(ConnectingOption(None, [None])) + port_inputs.append(ConnectingOption(None, [None])) + port_inputs.append(ConnectingOption(None, [None])) + port_inputs.append(ConnectingOption(None, [None])) + port_inputs.append(ConnectingOption(None, [None])) + port_inputs.append(ConnectingOption(None, [None])) + port_inputs.append(ConnectingOption(None, [None])) + port_inputs.append(ConnectingOption(None, [None])) + port_inputs.append(ConnectingOption(None, [None])) + port_inputs.append(ConnectingOption(None, [None])) + port_inputs.append(ConnectingOption(None, [None])) + port_inputs.append(ConnectingOption(None, [None])) + port_inputs.append(ConnectingOption(None, [None])) + port_inputs.append(ConnectingOption(None, [None])) + port_inputs.append(ConnectingOption(None, [None])) + port_inputs.append(ConnectingOption(None, [None])) + port_inputs.append(ConnectingOption(None, [None])) + + port_outputs = [] + port_outputs.append(ConnectingOption(None, [])) + port_outputs.append(ConnectingOption(None, [])) + port_outputs.append(ConnectingOption(None, [])) + port_outputs.append(ConnectingOption(None, [])) + port_outputs.append(ConnectingOption(None, [])) + port_outputs.append(ConnectingOption(None, [])) + port_outputs.append(ConnectingOption(None, [])) + port_outputs.append(ConnectingOption(None, [])) + port_outputs.append(ConnectingOption(None, [])) + port_outputs.append(ConnectingOption(None, [])) + port_outputs.append(ConnectingOption(None, [])) + port_outputs.append(ConnectingOption(None, [])) + port_outputs.append(ConnectingOption(None, [])) + port_outputs.append(ConnectingOption(None, [])) + port_outputs.append(ConnectingOption(None, [])) + port_outputs.append(ConnectingOption(None, [])) + port_outputs.append(ConnectingOption(None, [])) + port_outputs.append(ConnectingOption(None, [])) + port_outputs.append(ConnectingOption(None, [])) + + port = Primitive( + "PORT", + PrimitiveType.COMPONENT, + r"""{ + v1:IO + }""", + False, + False, + port_inputs, + port_outputs, + None, + None, + None, + ) + + library.add_io_entry(port) + + # MIXER - CONTINOUS FLOW ONE + + cf_mixer_inputs = [] + + cf_mixer_inputs.append(ConnectingOption(None, ["1"])) + cf_mixer_inputs.append(ConnectingOption(None, ["1"])) + cf_mixer_inputs.append(ConnectingOption(None, ["1"])) + cf_mixer_inputs.append(ConnectingOption(None, ["1"])) + cf_mixer_inputs.append(ConnectingOption(None, ["1"])) + cf_mixer_inputs.append(ConnectingOption(None, ["1"])) + cf_mixer_inputs.append(ConnectingOption(None, ["1"])) + cf_mixer_inputs.append(ConnectingOption(None, ["1"])) + cf_mixer_inputs.append(ConnectingOption(None, ["1"])) + cf_mixer_inputs.append(ConnectingOption(None, ["1"])) + + cf_mixer_outputs = [] + + cf_mixer_outputs.append(ConnectingOption(None, ["2"])) + + cf_mixer_loadings = [] + cf_mixer_carriers = [] + + cf_mixer = Primitive( + "MIXER", + PrimitiveType.COMPONENT, + r"""{ + v1:MIX + }""", + False, + False, + cf_mixer_inputs, + cf_mixer_outputs, + cf_mixer_loadings, + cf_mixer_carriers, + None, + ) + + library.add_operator_entry(cf_mixer, InteractionType.MIX) + + # MUX2 + + mux2_inputs = [] + mux2_inputs.append(ConnectingOption(None, ["1"])) + + mux2_outputs = [] + mux2_outputs.append(ConnectingOption(None, ["2"])) + mux2_outputs.append(ConnectingOption(None, ["3"])) + + mux2 = Primitive( + "MUX", + PrimitiveType.COMPONENT, + r"""{ + v1 { "DISTRIBUTE_OR", "or_1" }, + v1 -> vo1 { "DISTRIBUTE_OR", "or_1" }, + v1 -> vo2 { "DISTRIBUTE_OR", "or_1" } + } + """, + False, + False, + mux2_inputs, + mux2_outputs, + None, + None, + None, + ) + + library.add_entry(mux2) + + return library + + def generate_mars_library() -> MappingLibrary: library = MappingLibrary("mars") diff --git a/lfr/netlistgenerator/mappinglibrary.py b/lfr/netlistgenerator/mappinglibrary.py index 632efb2..2ab4021 100644 --- a/lfr/netlistgenerator/mappinglibrary.py +++ b/lfr/netlistgenerator/mappinglibrary.py @@ -50,6 +50,9 @@ def add_operator_entry( self.__technology_process_operators.append(primitive) self.__all_primitives[primitive.mint] = primitive + def add_entry(self, primitive: Primitive) -> None: + self.__all_primitives[primitive.mint] = primitive + def add_procedural_entry(self, primitive: ProceduralPrimitive) -> None: self.__procedural_primitves.append(primitive) self.__all_primitives[primitive.mint] = primitive diff --git a/poetry.lock b/poetry.lock index 0f390d5..81ddbc7 100644 --- a/poetry.lock +++ b/poetry.lock @@ -245,6 +245,14 @@ category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +[[package]] +name = "pyeda" +version = "0.28.0" +description = "Python Electronic Design Automation" +category = "main" +optional = false +python-versions = "*" + [[package]] name = "pyflakes" version = "2.2.0" @@ -347,7 +355,7 @@ python-versions = "*" [metadata] lock-version = "1.1" python-versions = "^3.8" -content-hash = "47b9e9e748442da58a34fc85a23c06f623e925fb196fb139724244c41b0614b6" +content-hash = "bbeb398ffdd62164f918b918c592ba24f01eaf08bedfed6515c7136494b12a86" [metadata.files] antlr4-python3-runtime = [ @@ -466,6 +474,10 @@ pycodestyle = [ {file = "pycodestyle-2.6.0-py2.py3-none-any.whl", hash = "sha256:2295e7b2f6b5bd100585ebcb1f616591b652db8a741695b3d8f5d28bdc934367"}, {file = "pycodestyle-2.6.0.tar.gz", hash = "sha256:c58a7d2815e0e8d7972bf1803331fb0152f867bd89adf8a01dfd55085434192e"}, ] +pyeda = [ + {file = "pyeda-0.28.0.tar.gz", hash = "sha256:07185f458d5d0b2ba5058da8b95dad6ab7684ceaf41237a25bcd3f005490f59d"}, + {file = "pyeda-0.28.0.zip", hash = "sha256:e34c7a6332d24914d1be524b3d9fe97feb165dbfe63473b3a112c1aeda2c002e"}, +] pyflakes = [ {file = "pyflakes-2.2.0-py2.py3-none-any.whl", hash = "sha256:0d94e0e05a19e57a99444b6ddcf9a6eb2e5c68d3ca1e98e90707af8152c90a92"}, {file = "pyflakes-2.2.0.tar.gz", hash = "sha256:35b2d75ee967ea93b55750aa9edbbf72813e06a66ba54438df2cfac9e3c27fc8"}, diff --git a/pyeda_test.py b/pyeda_test.py new file mode 100644 index 0000000..cc959f1 --- /dev/null +++ b/pyeda_test.py @@ -0,0 +1,13 @@ +import pyeda + +a, b, c, d = map(exprvar, "abcd") + +f0 = ~a & b | c & ~d +f1 = a >> b + +print(f0) +print(f1) + +f3 = OneHot(a, b, c, d) + +f1 = Or(~a & ~b & ~c, ~a & ~b & c, a & ~b & c, a & b & c, a & b & ~c) \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml index 01c76eb..90b4c2e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -25,6 +25,7 @@ tabulate = "^0.8.7" pymint = "^0.2.11" art = "^5.2" jsonschema = "^3.2.0" +pyeda = "^0.28.0" [tool.poetry.dev-dependencies] mypy = "^0.782" From 48cb82c7b4a7d27247d4fac7e42582aed382d370 Mon Sep 17 00:00:00 2001 From: Radhakrishna Sanka Date: Wed, 21 Jul 2021 10:44:39 -0400 Subject: [PATCH 33/36] Updated check on node constriant matching logic to ensure that matching doesn't happen if the match string doesn't have any constraints --- lfr/graphmatch/interface.py | 66 +++++++++++++++++++++++++++++++++---- 1 file changed, 59 insertions(+), 7 deletions(-) diff --git a/lfr/graphmatch/interface.py b/lfr/graphmatch/interface.py index 5f74107..c2698fd 100644 --- a/lfr/graphmatch/interface.py +++ b/lfr/graphmatch/interface.py @@ -1,5 +1,5 @@ from lfr.graphmatch.figmappingmatcher import FIGMappingMatcher -from typing import Dict +from typing import Any, Dict, List, Tuple from lfr.netlistgenerator.mappinglibrary import MappingLibrary from lfr.fig.fluidinteractiongraph import FluidInteractionGraph from lfr.graphmatch.matchpattern import MatchPattern @@ -64,7 +64,9 @@ def match_node_constraints( return True -def get_fig_matches(fig: FluidInteractionGraph, library: MappingLibrary): +def get_fig_matches( + fig: FluidInteractionGraph, library: MappingLibrary +) -> List[Tuple[str, Any]]: patterns: Dict[ str, MatchPattern ] = dict() # Store the mint and the match pattern object here @@ -91,15 +93,65 @@ def get_fig_matches(fig: FluidInteractionGraph, library: MappingLibrary): # This would be a match, figure out how to get the mapping from GM.mapping # Loop through each of the candates - # TODO - Compare the constraints on all the nodes for this for subgraph to + # Compare the constraints on all the nodes for this for subgraph to # confirm the match - if match_node_constraints( - fig, structural_template, semantic_information, subgraph, GM.mapping - ): - # TODO - Extract the specific mapping for the subgraph + + distribution_constaints = [] + distribution_annotations = [] + for node_filter in semantic_information.values(): + distribution_constaints.extend(node_filter.get_constriants()) + + for fig_id in subgraph.keys(): + fig_node = fig.get_fignode(fig_id) + distribution_annotations.extend(fig.get_fig_annotations(fig_node)) + + # Constraint matching Logic - This logic allows for annotated fig_nodes to + # be matched against match patterns that dont have any distribtion + # constraints. The 4 cases shown here describe the different ways in which + # this logic can work. + + # Case 1 - if subgraph has no distribution annotations and match template + # has no distribution constraints - SKIP | MATCH + + # Case 2 - if subgraph has no distribution annotations and match template + # has distribtion constraints - SKIP | NO-MATCH + + # Case 3 - if subgraph has distribution annotations and match + # template has no distribution constraints - SKIP | MATCH + + # Case 4 - if subgraph has distribution annotations and match template + # has distribution constraints - NO-SKIP | CHECK-MATCH + + # Case 1 + Case 3 Logic - We only need to check if the distribution + # constraints are zero (LOGIC REDUCTION) + if len(distribution_constaints) == 0: + # No distribution constraints, so we can skip node constraint matching print("Found Match: {}".format(mint)) print(subgraph) + # MATCH ret.append((mint, subgraph)) + # SKIP + continue + + # Case 2 Logic + if len(distribution_annotations) == 0 and len(distribution_constaints) > 0: + # NO-MATCH, SKIP + continue + + # Case 4 Logic + if len(distribution_annotations) > 0 and len(distribution_constaints) > 0: + # Check if the subgraph annotations matche the distribution constraints + if match_node_constraints( + fig, structural_template, semantic_information, subgraph, GM.mapping + ): + # TODO - Extract the specific mapping for the subgraph + print("Found Match: {}".format(mint)) + print(subgraph) + + ret.append((mint, subgraph)) + else: + # NO-MATCH, SKIP + continue return ret From 22e9320d71dfbcd25a287cf58499dbf0187d39c9 Mon Sep 17 00:00:00 2001 From: Radhakrishna Sanka Date: Tue, 27 Jul 2021 12:17:51 -0400 Subject: [PATCH 34/36] Updated the dist-relation matching algorithm to consider various cases and outlined todo's for cases we are not checking at the moment. We only consider it a match when its a bijective match. We are not considering a surjective match since we will have to add an algorithm to check for composability and satisfiability utilizing the library primitives. --- lfr/fig/annotation.py | 10 +- lfr/graphmatch/interface.py | 214 +++++++++++++++++++++++------- lfr/graphmatch/nodefilter.py | 2 +- lfr/netlistgenerator/generator.py | 4 +- 4 files changed, 174 insertions(+), 56 deletions(-) diff --git a/lfr/fig/annotation.py b/lfr/fig/annotation.py index 9af8830..d909407 100644 --- a/lfr/fig/annotation.py +++ b/lfr/fig/annotation.py @@ -36,6 +36,10 @@ def clear_items(self) -> None: def __hash__(self) -> int: return hash(hex(id(self))) + @property + def match_string(self) -> str: + raise NotImplementedError("Not implemented to the base disribution graph") + class ANDAnnotation(DistributeAnnotation): def __init__(self, id: str) -> None: @@ -43,7 +47,7 @@ def __init__(self, id: str) -> None: @property def match_string(self): - return "DISTRIBUTE-AND" + return "DISTRIBUTE_AND" class ORAnnotation(DistributeAnnotation): @@ -52,7 +56,7 @@ def __init__(self, id: str) -> None: @property def match_string(self): - return "DISTRIBUTE-OR" + return "DISTRIBUTE_OR" class NOTAnnotation(DistributeAnnotation): @@ -61,4 +65,4 @@ def __init__(self, id: str) -> None: @property def match_string(self): - return "DISTRIBUTE-NOT" + return "DISTRIBUTE_NOT" diff --git a/lfr/graphmatch/interface.py b/lfr/graphmatch/interface.py index c2698fd..22c3d35 100644 --- a/lfr/graphmatch/interface.py +++ b/lfr/graphmatch/interface.py @@ -1,66 +1,177 @@ +from lfr.fig.annotation import DistributeAnnotation from lfr.graphmatch.figmappingmatcher import FIGMappingMatcher -from typing import Any, Dict, List, Tuple +from typing import Any, Dict, FrozenSet, List, Tuple from lfr.netlistgenerator.mappinglibrary import MappingLibrary from lfr.fig.fluidinteractiongraph import FluidInteractionGraph from lfr.graphmatch.matchpattern import MatchPattern from lfr.graphmatch.nodefilter import NodeFilter -def match_node_constraints( +def bijective_match_node_constraints( fig: FluidInteractionGraph, - structural_template, semantic_information: Dict[str, NodeFilter], - subgraph, - mapping, + subgraph: Dict[str, str], ) -> bool: # TODO - Check if the constraints match for the subgraph - # Loop through each of the unique constraints in the overall semantic information - # Check if the 1) Constraint type matches, 2) if the same nodes in the GM.mapping - # match the ones denoted by the subgraph LFR - - # Generate new IDs for each of the nodes from the fig and the netlist matches in - # the mapping + # STEP 1 - generate new unique names for each node to simplify the matching + # algorihtm + + # STEP 2 - Create a dictionary match_node_dict that has keys which are the set of + # all the renamed match graph node ID's and values to be the list of all the + # relationships for those nodes + + # STEP 3 - Create a dictionary fig_node_dict that has keys which are the set of all + # the renamed FIGNode ID's and values to be the list of all the relationships for + # those nodes (use DISTRIBUTE ANNOTATION MATCH STRING) + + # STEP 4 - Check if the dictionaries match : + + # RETURN TRUE CONDITIONS + # 1 - Exact match in the final dictionary (the mapping between the two dictionary + # keys is a bijection) + # 2 - TODO - Mapping between the two dictionary keys is a surjection (This will + # allow us to have composable functiontions) + + # RETIRN FALSE CONDITIONS: + # 1 - Cannot find node in one of the annotations (this will limit the types of + # matches) and not allow composable matches for disribution annotations networks + # 2 - Final dictionary entries do not match: + # 2.1 - Every entry in teh match node dictionary should be present in the + # fig_node_dict + # 2.2 - Assuming coverage is limited to the exact match subgraph in the fig, we + # need to ensure that + + # Step 1 - Generate new unique names for each node to simplify the matching + # algorihtm # Loop through each of the nodes in the fig and the mapping new_ids = {} - new_id_counter = 0 - for key, value in mapping.items(): - new_ids[key] = str(new_id_counter) - new_ids[value] = str(new_id_counter) - new_id_counter += 1 - - # Extract the subgraph pairings in the to know what fig node matches what matchnode - fig_distribution_set = {} - reggie_distribution_annotations = {} - - for fig_id, reggie_id in mapping.items(): - - new_id = new_ids[reggie_id] - node_filter = semantic_information[reggie_id] - - # Skip getting the constraints for the match nodes - if len(node_filter.get_constriants()) == 0: - continue - - for constriant in node_filter.get_constriants(): - if new_id not in reggie_distribution_annotations.keys(): - reggie_distribution_annotations[new_id] = {constriant[1]} - else: - reggie_distribution_annotations[new_id].add(constriant[1]) - - new_id = new_ids[fig_id] - - try: - annotations = fig.get_fig_annotations(fig_id) - except KeyError: - print("Missing annotation for fig node: {}".format(fig_id)) - continue - + # new_id_counter = 0 + for key, value in subgraph.items(): + # new_ids[key] = str(new_id_counter) + # new_ids[value] = str(new_id_counter) + # new_id_counter += 1 + # TODO - easier to debug revert back to counter based system + new_ids[key] = "{}_{}".format(key, value) + new_ids[value] = "{}_{}".format(key, value) + + # Step 2 - Create a dictionary match_node_dict that has keys which are the set of + # all the renamed match graph node ID's and values to be the list of all the + # relationships for those nodes + match_node_dict: Dict[FrozenSet[str], List[str]] = {} + + # Create a dictionary to keep all the match_node relationships + # Stores {relationship_id: [matchnode ids]} + match_node_relationships: Dict[str, List[str]] = {} + # Create a dictionary to keep all the node relationship types + # Stores {relationship_id: relationship_type} + node_relationship_types: Dict[str, str] = {} + + # Loop through each of the NodeFilters in the semantic information + for match_node_id, node_filter in semantic_information.items(): + # Put the relationship_id and relationship_type into the coresponding dicts + # Go through each of the tuples in the node_filter constraints + for relationship_type, relationship_id in node_filter.get_constraints(): + # If the relationship_id is not in the relationship_id dict, add it + if relationship_id not in match_node_relationships: + match_node_relationships[relationship_id] = [] + node_relationship_types[relationship_id] = relationship_type + # Add the match_node_id to the list of match_node_ids for the + # relationship_id (We add the newly generated ids to the match_node_dict). + match_node_relationships[relationship_id].append(new_ids[match_node_id]) + + # Populate the match_node_dict with the data from the match_node_relationships + for ( + relationship_id, + relationship_match_node_ids, + ) in match_node_relationships.items(): + # generate a set of all the match_node_ids for the relationship_id + match_node_ids_set = frozenset(relationship_match_node_ids) + if match_node_ids_set not in match_node_dict: + match_node_dict[match_node_ids_set] = [] + # Add the relationship type into the list corresponding to the ids set + match_node_dict[match_node_ids_set].append( + node_relationship_types[relationship_id] + ) + + # Step 3 - Create a dictionary fig_node_dict that has keys which are the set of all + fig_node_dict: Dict[FrozenSet[str], List[str]] = {} + # Loop throught the fignodes in the subgraph find the corresponding annotations and + # populate the fig_node_dict + checked_annotations = [] + for fig_node_id in subgraph.keys(): + fig_node = fig.get_fignode(fig_node_id) + annotations = fig.get_fig_annotations(fig_node) + # Generate the set of all the items in the fig_node annotation for annotation in annotations: - if new_id not in fig_distribution_set.keys(): - fig_distribution_set[new_id] = {annotation.id} + # Check if the annotation has already been checked + if annotations not in checked_annotations: + checked_annotations.append(annotations) else: - fig_distribution_set[new_id].add(annotation.id) + continue + + annotation_set = set() + skip_annotation = False + for item in annotation.get_items(): + if isinstance(item, DistributeAnnotation): + raise NotImplementedError( + "Need to figure out how to define/process nested relationship" + ) + else: + # TODO - Handle scenarios where the Item is not found in the + # mapping scope + if item[0].ID in new_ids.keys() and item[1].ID in new_ids.keys(): + annotation_set.add(new_ids[item[0].ID]) + annotation_set.add(new_ids[item[1].ID]) + else: + print( + 'Warning! Rejecting annotation "{}" for consideration in' + " DISTRIBUTE RELATIONSHIP Matching since it has items not" + " covered by the Primitive".format(annotation) + ) + skip_annotation = True + break + + if skip_annotation: + # Skip adding the annotation since there were fig items outside of the + # current mapping domain + continue + # Add the annotation_set to the fig_node_dict with the corresponding + # annotation type + if frozenset(annotation_set) not in fig_node_dict: + fig_node_dict[frozenset(annotation_set)] = [] + + fig_node_dict[frozenset(annotation_set)].append(annotation.match_string) + + # Step 4 - Check if the dictionaries match (is a bijection) + # Step 4.1 - Check if the relatships between the fig and the match networks are + # surjective + # Step 4.1.1 - Check if each of the keys in the match_node_dict is in the + # fig_node_dict + for ids_set in match_node_dict.keys(): + if ids_set not in fig_node_dict: + return False + # Step 4.1.2 - Check if each of the values in the fig_node_dict is in the + # match_node_dict + fig_rels_list = fig_node_dict[ids_set].sort() + match_rels_list = match_node_dict[ids_set].sort() + if fig_rels_list != match_rels_list: + return False + # Step 4.2 - Check if the relationships between the fig and the match networks are + # injective + # Step 4.2.1 - Check if each of the keys in the fig_node_dict is in the + # match_node_dict + for ids_set in fig_node_dict.keys(): + if ids_set not in match_node_dict: + return False + # Step 4.2.2 - Check if each of the values in the match_node_dict is in the + # fig_node_dict + fig_rels_list = fig_node_dict[ids_set].sort() + match_rels_list = match_node_dict[ids_set].sort() + if fig_rels_list != match_rels_list: + return False + + # If we get to this point, then the match network is a bijection return True @@ -99,7 +210,7 @@ def get_fig_matches( distribution_constaints = [] distribution_annotations = [] for node_filter in semantic_information.values(): - distribution_constaints.extend(node_filter.get_constriants()) + distribution_constaints.extend(node_filter.get_constraints()) for fig_id in subgraph.keys(): fig_node = fig.get_fignode(fig_id) @@ -142,8 +253,11 @@ def get_fig_matches( # Case 4 Logic if len(distribution_annotations) > 0 and len(distribution_constaints) > 0: # Check if the subgraph annotations matche the distribution constraints - if match_node_constraints( - fig, structural_template, semantic_information, subgraph, GM.mapping + # TODO - Also expand this algorithm to allow for surjective matches. + # This would allow us to verify the composability of the matches with + # the library + if bijective_match_node_constraints( + fig, semantic_information, subgraph ): # TODO - Extract the specific mapping for the subgraph print("Found Match: {}".format(mint)) diff --git a/lfr/graphmatch/nodefilter.py b/lfr/graphmatch/nodefilter.py index 8376e3a..98f123b 100644 --- a/lfr/graphmatch/nodefilter.py +++ b/lfr/graphmatch/nodefilter.py @@ -24,5 +24,5 @@ def is_valid_attribute_type(self, attributestring: str) -> bool: raise NotImplementedError("Currently do not support node attributes") return True - def get_constriants(self) -> List[Tuple[str, str]]: + def get_constraints(self) -> List[Tuple[str, str]]: return self._node_constraints diff --git a/lfr/netlistgenerator/generator.py b/lfr/netlistgenerator/generator.py index 60a82b0..fc569b7 100644 --- a/lfr/netlistgenerator/generator.py +++ b/lfr/netlistgenerator/generator.py @@ -1035,8 +1035,8 @@ def generate(module: Module, library: MappingLibrary) -> MINTDevice: if io_ref.type is IOType.CONTROL: continue for io in io_ref.vector_ref: - cn = ConstructionNode(io.id) - sub_graph = module.FIG.subgraph(io.id) + cn = ConstructionNode(io.ID) + sub_graph = module.FIG.subgraph(io.ID) mapping_candidate = library.get_default_IO() mapping_option = MappingOption(mapping_candidate, sub_graph) cn.add_mapping_option(mapping_option) From d1f6e86c0472c259427fe8b61601c0dcc5004b4a Mon Sep 17 00:00:00 2001 From: Radhakrishna Sanka Date: Tue, 31 Aug 2021 15:59:55 -0400 Subject: [PATCH 35/36] Changed how we return the mathes --- lfr/graphmatch/interface.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/lfr/graphmatch/interface.py b/lfr/graphmatch/interface.py index 22c3d35..b58036b 100644 --- a/lfr/graphmatch/interface.py +++ b/lfr/graphmatch/interface.py @@ -187,12 +187,12 @@ def get_fig_matches( # TODO - Retrun the networkx subgraph views of the of the FIG # Step 1 - Generate the match candidates by running the subgraph isomerism for all # the items stored in the library - for (mint, match_pattern_string) in library.get_match_patterns(): + for (minty_uid, match_pattern_string) in library.get_match_patterns(): if match_pattern_string == "" or match_pattern_string is None: - print("Warning ! - Missing match string for mint- {}".format(mint)) + print("Warning ! - Missing match string for mint- {}".format(minty_uid)) continue pattern = MatchPattern(match_pattern_string) - patterns[mint] = pattern + patterns[minty_uid] = pattern structural_template = pattern.get_structural_template() semantic_information = pattern.get_semantic_template() GM = FIGMappingMatcher(fig, structural_template, semantic_information) @@ -237,11 +237,11 @@ def get_fig_matches( # constraints are zero (LOGIC REDUCTION) if len(distribution_constaints) == 0: # No distribution constraints, so we can skip node constraint matching - print("Found Match: {}".format(mint)) + print("Found Match: {}".format(minty_uid)) print(subgraph) # MATCH - ret.append((mint, subgraph)) + ret.append((minty_uid, subgraph)) # SKIP continue @@ -260,10 +260,10 @@ def get_fig_matches( fig, semantic_information, subgraph ): # TODO - Extract the specific mapping for the subgraph - print("Found Match: {}".format(mint)) + print("Found Match: {}".format(minty_uid)) print(subgraph) - ret.append((mint, subgraph)) + ret.append((minty_uid, subgraph)) else: # NO-MATCH, SKIP continue From 400ab25205bbbcc0a635c790a8c2b139434a0ecb Mon Sep 17 00:00:00 2001 From: Radhakrishna Sanka Date: Mon, 23 Oct 2023 16:29:59 +0530 Subject: [PATCH 36/36] Update with full refactor for ScripSlate (#50) TODOs: * TODO - We need to update the channel generation pieces for the channels. * TODO - Finish up the mapping/match generation for the flow subgraphs. * TODO - Need to ensure that the netlist generation is working smoothly * TODO - Need to update the fluid interaction graph with a serializer to have the semantic matching as a part of the equality checking. Changelist * Updated most of the base classes * Some more updates, added not implemented errors everywhere in the flow * Removed Todo * Started updating variant generation * Fixed issue with importing networks (dunno how this came about) * Moved all the command line code to code to API * Updated for temporary changes, still need to complete stuff * Updated the dafd afa * updated the codebase for the updated mint and parchmint libraries * Updated both the fig twig and lfr antlrgenerators for the new runtime * Created a variant tree data structure to handle the adding/removing of the variants * Updated the packages * Updated the variant generation process * The new process uses a data structure called the 'VariantTree'. This is a dynamic structure that keeps track of the different 'ConstructionGraph' that gets generated during the technology mapping process. * Updated the ordering of the matches to reduce the potential number of variants. * Implemented FIG simplification * Updated the graph edge generation. * Put in a commit for saving all the changes. Need to update the branch for a fully functioning flow * Format code with black and isort (#51) Co-authored-by: deepsource-autofix[bot] <62050782+deepsource-autofix[bot]@users.noreply.github.com> * Updated all the dependencies * removed the submodule before readding it * Removed the old testcases sub repo * Updated the submodule to track the branch * Updated the tracking for the submodules * Format code with black and isort * Fixed issue Diamond chamber incorrect mint naming that was crashing primitives server (not sure why) * Format code with black and isort * Updated the dev container * Fixes/mypy cleanup (#52) * Updated the code with some more cleanup / attempting cleanup a bit more * Some project management fixes * Some minimal Fixes (avoiding vector and vector range for the time being. Those will get tests) * Finally fixed mypy errors * Cleaned up a ton of issues identified by mypy * updated some mypysettings * Fixed issues with circular references * Reenabled the iotype interface * reverted the annotation check in concatenation * updated the ignore pattern for deep source --------- Co-authored-by: deepsource-autofix[bot] <62050782+deepsource-autofix[bot]@users.noreply.github.com> * Caught the issue where ionode type was being overridden. * Updated the docs on fignodes * Updated the vector class * Moved the get_range method on vector out of the class to prevent circular imports * Updated the vector and Vector range definitions * Format code with black and isort * Added basic expressions as test cases. Will continue to increase test cases here for compiler coverage. Benchmarks will be used for final designs. But strategic test cases will be placed here. * Updated the sub repo refs * Updated the main api not to throw errors * Added the dropx benchmarks and the corresponding MINT reference cases. * Broke up the compile_lfr() into parts so that we can use it. * Updated the benchmarks * Updated the results data for dropx * Updated the tests / but it fails pretty badly * Updated the fig gen test * Updated the fluid interaction graph * Updated a few things along the way * Updated the error messages * Updated the netlist generator to have a connection primitive generator and be able to create the netlist. * Cleaned up print graph signature * Updated the print graph * Updated the code to include the variant folder generation since the outputs were getting overwritten. * Added basic skeleton code for doing validation against genstrategy rules. * Updated parchmint and pymint references --------- Co-authored-by: deepsource-autofix[bot] <62050782+deepsource-autofix[bot]@users.noreply.github.com> --- .deepsource.toml | 2 +- .devcontainer/Dockerfile | 47 +- .devcontainer/devcontainer.json | 36 +- .gitattributes | 1 + .gitignore | 3 +- .gitmodules | 15 +- .vscode/launch.json | 2 +- .vscode/settings.json | 18 +- dafd | 1 + lfr/antlrgen/lfr/lfrX.interp | 2 +- lfr/antlrgen/lfr/lfrXLexer.interp | 2 +- lfr/antlrgen/lfr/lfrXLexer.py | 6411 ++++++++++++- lfr/antlrgen/lfr/lfrXListener.py | 360 +- lfr/antlrgen/lfr/lfrXParser.py | 8296 +++++++++++++---- lfr/antlrgen/lfr/lfrXVisitor.py | 218 +- lfr/antlrgen/reggie/reggie.interp | 2 +- lfr/antlrgen/reggie/reggieLexer.interp | 2 +- lfr/antlrgen/reggie/reggieLexer.py | 1331 ++- lfr/antlrgen/reggie/reggieListener.py | 8 +- lfr/antlrgen/reggie/reggieParser.py | 1965 +++- lfr/antlrgen/reggie/reggieVisitor.py | 7 +- lfr/api.py | 192 + lfr/cmdline.py | 118 +- lfr/compiler/distribute/distributeblock.py | 3 +- lfr/compiler/distribute/statetable.py | 27 +- lfr/compiler/language/concatenation.py | 35 +- lfr/compiler/language/fluidexpression.py | 8 +- lfr/compiler/language/vector.py | 43 +- lfr/compiler/language/vectorrange.py | 60 +- lfr/compiler/module.py | 37 +- lfr/compiler/moduleio.py | 12 +- lfr/distBlockListener.py | 12 +- lfr/fig/annotation.py | 2 + lfr/fig/autocomplete.py | 8 + lfr/fig/fignode.py | 159 +- lfr/fig/fluidinteractiongraph.py | 335 +- lfr/fig/interaction.py | 26 +- lfr/fig/simplification.py | 135 + lfr/graphmatch/figmappingmatcher.py | 4 +- lfr/graphmatch/interface.py | 76 +- lfr/graphmatch/matchpattern.py | 14 +- lfr/graphmatch/matchpatterngenerator.py | 13 +- lfr/graphmatch/nodefilter.py | 1 + lfr/lfrbaseListener.py | 44 +- lfr/moduleinstanceListener.py | 8 +- lfr/netlistgenerator/__init__.py | 8 + lfr/netlistgenerator/connectingoption.py | 2 +- lfr/netlistgenerator/connection_primitive.py | 37 + .../constructiongraph/__init__.py | 2 + .../constructiongraph/constructiongraph.py | 160 + .../constructiongraph/constructionnode.py | 247 + .../constructiongraph/edge_generation.py | 285 + .../constructiongraph/variant_criteria.py | 25 + .../constructiongraph/variant_generator.py | 225 + .../constructiongraph/varianttree.py | 236 + lfr/netlistgenerator/dafdadapter.py | 23 +- lfr/netlistgenerator/explicitmapping.py | 8 +- lfr/netlistgenerator/explicitmappingoption.py | 3 +- lfr/netlistgenerator/flownetworkmatching.py | 89 + .../gen_strategies/dropxstrategy.py | 328 +- lfr/netlistgenerator/gen_strategies/dummy.py | 17 +- .../gen_strategies/genstrategy.py | 185 +- lfr/netlistgenerator/gen_strategies/mars.py | 22 - .../gen_strategies/marsstrategy.py | 150 +- lfr/netlistgenerator/generator.py | 1659 +--- lfr/netlistgenerator/mappinglibrary.py | 191 +- .../mappinglibrary_generator.py | 936 ++ lfr/netlistgenerator/mappingoption.py | 9 +- lfr/netlistgenerator/namegenerator.py | 42 +- lfr/netlistgenerator/netlist_generation.py | 161 + lfr/netlistgenerator/netlistsizor.py | 1 + lfr/netlistgenerator/networkmappingoption.py | 2 +- .../packaged_libraries/dropx.py | 3 +- lfr/netlistgenerator/primitive.py | 203 +- .../gradient_generator.py | 2 +- .../procedural_component_algorithms/mux.py | 2 +- .../procedural_component_algorithms/mux3d.py | 2 +- .../transposer.py | 2 +- .../procedural_component_algorithms/tree.py | 2 +- .../procedural_component_algorithms/ytree.py | 12 +- lfr/notes.md | 29 + lfr/parameters.py | 1 + lfr/postProcessListener.py | 25 +- lfr/postprocessor/constraints.py | 86 +- lfr/preprocessor.py | 37 +- lfr/utils.py | 52 +- library/mars.json | 2 +- .../constructiongraph-old.py | 10 +- .../constructionnode-old.py | 7 +- .../dafdadapter-old.py | 5 +- old-stuff/dropxstrategy-old.py | 296 + old-stuff/dummy-old.py | 9 + .../generator-old.py | 70 +- old-stuff/genstrategy-old.py | 130 + old-stuff/mars-old.py | 24 + old-stuff/marsstrategy-old.py | 158 + poetry.lock | 2790 +++++- pyeda_test.py | 2 +- pylfr.code-workspace | 14 + pymint | 1 + pyparchmint | 1 + pyproject.toml | 62 +- scripts.py | 24 + scripts/all.sh | 40 +- scripts/dropx.sh | 4 +- tests/__init__.py | 0 tests/conftest.py | 15 + tests/data/DropX/dx1.lfr | 21 + tests/data/DropX/dx10.lfr | 11 + tests/data/DropX/dx11.lfr | 25 + tests/data/DropX/dx12.lfr | 23 + tests/data/DropX/dx13.lfr | 36 + tests/data/DropX/dx14.lfr | 21 + tests/data/DropX/dx15.lfr | 9 + tests/data/DropX/dx2.lfr | 18 + tests/data/DropX/dx3.lfr | 21 + tests/data/DropX/dx4.lfr | 22 + tests/data/DropX/dx5.lfr | 18 + tests/data/DropX/dx6.lfr | 18 + tests/data/DropX/dx7.lfr | 13 + tests/data/DropX/dx8.lfr | 25 + tests/data/DropX/dx9.lfr | 17 + tests/data/DropX/figs/dx1.dot | 15 + tests/data/DropX/figs/dx1.dot.pdf | Bin 0 -> 12307 bytes tests/data/DropX/figs/dx10.dot | 10 + tests/data/DropX/figs/dx10.dot.pdf | Bin 0 -> 10169 bytes tests/data/DropX/figs/dx11.dot | 19 + tests/data/DropX/figs/dx11.dot.pdf | Bin 0 -> 17165 bytes tests/data/DropX/figs/dx12.dot | 17 + tests/data/DropX/figs/dx12.dot.pdf | Bin 0 -> 13733 bytes tests/data/DropX/figs/dx13.dot | 31 + tests/data/DropX/figs/dx13.dot.pdf | Bin 0 -> 15551 bytes tests/data/DropX/figs/dx14.dot | 21 + tests/data/DropX/figs/dx14.dot.pdf | Bin 0 -> 14899 bytes tests/data/DropX/figs/dx15.dot | 8 + tests/data/DropX/figs/dx15.dot.pdf | Bin 0 -> 10306 bytes tests/data/DropX/figs/dx2.dot | 16 + tests/data/DropX/figs/dx2.dot.pdf | Bin 0 -> 13040 bytes tests/data/DropX/figs/dx3.dot | 17 + tests/data/DropX/figs/dx3.dot.pdf | Bin 0 -> 11892 bytes tests/data/DropX/figs/dx4.dot | 26 + tests/data/DropX/figs/dx4.dot.pdf | Bin 0 -> 15500 bytes tests/data/DropX/figs/dx5.dot | 19 + tests/data/DropX/figs/dx5.dot.pdf | Bin 0 -> 14301 bytes tests/data/DropX/figs/dx6.dot | 14 + tests/data/DropX/figs/dx6.dot.pdf | Bin 0 -> 11512 bytes tests/data/DropX/figs/dx7.dot | 18 + tests/data/DropX/figs/dx7.dot.pdf | Bin 0 -> 12901 bytes tests/data/DropX/figs/dx8.dot | 25 + tests/data/DropX/figs/dx8.dot.pdf | Bin 0 -> 14916 bytes tests/data/DropX/figs/dx9.dot | 15 + tests/data/DropX/figs/dx9.dot.pdf | Bin 0 -> 12325 bytes tests/data/DropX/netlists/Standard Sizes.md | 101 + tests/data/DropX/netlists/dx10_ref.mint | 68 + tests/data/DropX/netlists/dx11_ref.mint | 135 + tests/data/DropX/netlists/dx12_ref.mint | 73 + tests/data/DropX/netlists/dx13_ref.mint | 138 + tests/data/DropX/netlists/dx14_ref.mint | 73 + tests/data/DropX/netlists/dx15_ref.mint | 43 + tests/data/DropX/netlists/dx1_ref.mint | 42 + tests/data/DropX/netlists/dx2_ref.mint | 59 + tests/data/DropX/netlists/dx3_ref.mint | 65 + tests/data/DropX/netlists/dx4_ref.mint | 131 + tests/data/DropX/netlists/dx5_ref.mint | 112 + tests/data/DropX/netlists/dx6_ref.mint | 51 + tests/data/DropX/netlists/dx7_ref.mint | 113 + tests/data/DropX/netlists/dx8_ref.mint | 136 + tests/data/DropX/netlists/dx9_ref.mint | 101 + tests/data/Expressions/expression1.lfr | 10 + tests/data/Expressions/expression10.lfr | 11 + tests/data/Expressions/expression11.lfr | 9 + tests/data/Expressions/expression12.lfr | 9 + tests/data/Expressions/expression13.lfr | 11 + tests/data/Expressions/expression14.lfr | 6 + tests/data/Expressions/expression15.lfr | 9 + tests/data/Expressions/expression2.lfr | 9 + tests/data/Expressions/expression3.lfr | 9 + tests/data/Expressions/expression4.lfr | 9 + tests/data/Expressions/expression5.lfr | 9 + tests/data/Expressions/expression6.lfr | 10 + tests/data/Expressions/expression7.lfr | 10 + tests/data/Expressions/expression8.lfr | 11 + tests/data/Expressions/expression9.lfr | 9 + tests/pylfr/e2e/__init__.py | 0 tests/pylfr/e2e/compiler/__init__.py | 0 tests/pylfr/e2e/compiler/test_devicegen.py | 0 tests/pylfr/e2e/compiler/test_expressions.py | 215 + tests/pylfr/e2e/compiler/test_figgen.py | 400 + tests/pylfr/e2e/compiler/test_mintgen.py | 32 + 189 files changed, 26258 insertions(+), 5816 deletions(-) create mode 100644 .gitattributes create mode 160000 dafd create mode 100644 lfr/api.py create mode 100644 lfr/fig/autocomplete.py create mode 100644 lfr/fig/simplification.py create mode 100644 lfr/netlistgenerator/connection_primitive.py create mode 100644 lfr/netlistgenerator/constructiongraph/__init__.py create mode 100644 lfr/netlistgenerator/constructiongraph/constructiongraph.py create mode 100644 lfr/netlistgenerator/constructiongraph/constructionnode.py create mode 100644 lfr/netlistgenerator/constructiongraph/edge_generation.py create mode 100644 lfr/netlistgenerator/constructiongraph/variant_criteria.py create mode 100644 lfr/netlistgenerator/constructiongraph/variant_generator.py create mode 100644 lfr/netlistgenerator/constructiongraph/varianttree.py create mode 100644 lfr/netlistgenerator/flownetworkmatching.py delete mode 100644 lfr/netlistgenerator/gen_strategies/mars.py create mode 100644 lfr/netlistgenerator/mappinglibrary_generator.py create mode 100644 lfr/netlistgenerator/netlist_generation.py create mode 100644 lfr/notes.md rename lfr/netlistgenerator/constructiongraph.py => old-stuff/constructiongraph-old.py (99%) rename lfr/netlistgenerator/constructionnode.py => old-stuff/constructionnode-old.py (99%) rename {lfr/netlistgenerator => old-stuff}/dafdadapter-old.py (99%) create mode 100644 old-stuff/dropxstrategy-old.py create mode 100644 old-stuff/dummy-old.py rename generator-old.py => old-stuff/generator-old.py (98%) create mode 100644 old-stuff/genstrategy-old.py create mode 100644 old-stuff/mars-old.py create mode 100644 old-stuff/marsstrategy-old.py create mode 100644 pylfr.code-workspace create mode 160000 pymint create mode 160000 pyparchmint create mode 100644 scripts.py create mode 100644 tests/__init__.py create mode 100644 tests/conftest.py create mode 100644 tests/data/DropX/dx1.lfr create mode 100644 tests/data/DropX/dx10.lfr create mode 100644 tests/data/DropX/dx11.lfr create mode 100644 tests/data/DropX/dx12.lfr create mode 100644 tests/data/DropX/dx13.lfr create mode 100644 tests/data/DropX/dx14.lfr create mode 100644 tests/data/DropX/dx15.lfr create mode 100644 tests/data/DropX/dx2.lfr create mode 100644 tests/data/DropX/dx3.lfr create mode 100644 tests/data/DropX/dx4.lfr create mode 100644 tests/data/DropX/dx5.lfr create mode 100644 tests/data/DropX/dx6.lfr create mode 100644 tests/data/DropX/dx7.lfr create mode 100644 tests/data/DropX/dx8.lfr create mode 100644 tests/data/DropX/dx9.lfr create mode 100644 tests/data/DropX/figs/dx1.dot create mode 100644 tests/data/DropX/figs/dx1.dot.pdf create mode 100644 tests/data/DropX/figs/dx10.dot create mode 100644 tests/data/DropX/figs/dx10.dot.pdf create mode 100644 tests/data/DropX/figs/dx11.dot create mode 100644 tests/data/DropX/figs/dx11.dot.pdf create mode 100644 tests/data/DropX/figs/dx12.dot create mode 100644 tests/data/DropX/figs/dx12.dot.pdf create mode 100644 tests/data/DropX/figs/dx13.dot create mode 100644 tests/data/DropX/figs/dx13.dot.pdf create mode 100644 tests/data/DropX/figs/dx14.dot create mode 100644 tests/data/DropX/figs/dx14.dot.pdf create mode 100644 tests/data/DropX/figs/dx15.dot create mode 100644 tests/data/DropX/figs/dx15.dot.pdf create mode 100644 tests/data/DropX/figs/dx2.dot create mode 100644 tests/data/DropX/figs/dx2.dot.pdf create mode 100644 tests/data/DropX/figs/dx3.dot create mode 100644 tests/data/DropX/figs/dx3.dot.pdf create mode 100644 tests/data/DropX/figs/dx4.dot create mode 100644 tests/data/DropX/figs/dx4.dot.pdf create mode 100644 tests/data/DropX/figs/dx5.dot create mode 100644 tests/data/DropX/figs/dx5.dot.pdf create mode 100644 tests/data/DropX/figs/dx6.dot create mode 100644 tests/data/DropX/figs/dx6.dot.pdf create mode 100644 tests/data/DropX/figs/dx7.dot create mode 100644 tests/data/DropX/figs/dx7.dot.pdf create mode 100644 tests/data/DropX/figs/dx8.dot create mode 100644 tests/data/DropX/figs/dx8.dot.pdf create mode 100644 tests/data/DropX/figs/dx9.dot create mode 100644 tests/data/DropX/figs/dx9.dot.pdf create mode 100644 tests/data/DropX/netlists/Standard Sizes.md create mode 100644 tests/data/DropX/netlists/dx10_ref.mint create mode 100644 tests/data/DropX/netlists/dx11_ref.mint create mode 100644 tests/data/DropX/netlists/dx12_ref.mint create mode 100644 tests/data/DropX/netlists/dx13_ref.mint create mode 100644 tests/data/DropX/netlists/dx14_ref.mint create mode 100644 tests/data/DropX/netlists/dx15_ref.mint create mode 100644 tests/data/DropX/netlists/dx1_ref.mint create mode 100644 tests/data/DropX/netlists/dx2_ref.mint create mode 100644 tests/data/DropX/netlists/dx3_ref.mint create mode 100644 tests/data/DropX/netlists/dx4_ref.mint create mode 100644 tests/data/DropX/netlists/dx5_ref.mint create mode 100644 tests/data/DropX/netlists/dx6_ref.mint create mode 100644 tests/data/DropX/netlists/dx7_ref.mint create mode 100644 tests/data/DropX/netlists/dx8_ref.mint create mode 100644 tests/data/DropX/netlists/dx9_ref.mint create mode 100644 tests/data/Expressions/expression1.lfr create mode 100644 tests/data/Expressions/expression10.lfr create mode 100644 tests/data/Expressions/expression11.lfr create mode 100644 tests/data/Expressions/expression12.lfr create mode 100644 tests/data/Expressions/expression13.lfr create mode 100644 tests/data/Expressions/expression14.lfr create mode 100644 tests/data/Expressions/expression15.lfr create mode 100644 tests/data/Expressions/expression2.lfr create mode 100644 tests/data/Expressions/expression3.lfr create mode 100644 tests/data/Expressions/expression4.lfr create mode 100644 tests/data/Expressions/expression5.lfr create mode 100644 tests/data/Expressions/expression6.lfr create mode 100644 tests/data/Expressions/expression7.lfr create mode 100644 tests/data/Expressions/expression8.lfr create mode 100644 tests/data/Expressions/expression9.lfr create mode 100644 tests/pylfr/e2e/__init__.py create mode 100644 tests/pylfr/e2e/compiler/__init__.py create mode 100644 tests/pylfr/e2e/compiler/test_devicegen.py create mode 100644 tests/pylfr/e2e/compiler/test_expressions.py create mode 100644 tests/pylfr/e2e/compiler/test_figgen.py create mode 100644 tests/pylfr/e2e/compiler/test_mintgen.py diff --git a/.deepsource.toml b/.deepsource.toml index b8aa7aa..6e6717e 100644 --- a/.deepsource.toml +++ b/.deepsource.toml @@ -1,6 +1,6 @@ version = 1 -exclude_patterns = ["**/antlrgen/*","**/BitVector.py"] +exclude_patterns = ["lfr/antlrgen/**.*","**/BitVector.py"] [[analyzers]] name = "python" diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index d9be9cb..ce5d0c6 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -1,44 +1,29 @@ -# See here for image contents: https://github.com/microsoft/vscode-dev-containers/tree/v0.166.1/containers/ubuntu/.devcontainer/base.Dockerfile +# See here for image contents: https://github.com/microsoft/vscode-dev-containers/tree/v0.194.3/containers/ubuntu/.devcontainer/base.Dockerfile -# [Choice] Ubuntu version: bionic, focal +# [Choice] Ubuntu version: hirsute, bionic, focal ARG VARIANT="focal" FROM mcr.microsoft.com/vscode/devcontainers/base:0-${VARIANT} -# FROM ubuntu:20.04 ENV PYTHONFAULTHANDLER=1 \ PYTHONUNBUFFERED=1 \ PYTHONHASHSEED=random \ PIP_NO_CACHE_DIR=off \ - # PIP_DISABLE_PIP_VERSION_CHECK=on \ + PIP_DISABLE_PIP_VERSION_CHECK=on \ PIP_DEFAULT_TIMEOUT=100 \ - POETRY_VERSION=1.0.0 + POETRY_VERSION=1.2.0 # [Optional] Uncomment this section to install additional OS packages. -RUN export DEBIAN_FRONTEND=noninteractive \ - && sudo apt-get update \ - && apt-get -y install --no-install-recommends build-essential curl software-properties-common \ - python3-pip git make build-essential python-dev libssl-dev zlib1g-dev \ - libbz2-dev libreadline-dev libsqlite3-dev curl libffi-dev \ - graphviz libgraphviz-dev \ - libcairo2-dev pkg-config python3-dev +RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \ + && apt-get -y install --no-install-recommends curl build-essential \ + software-properties-common python3-pip make build-essential \ + python-dev libssl-dev zlib1g-dev libbz2-dev libreadline-dev \ + libsqlite3-dev curl libffi-dev redis-server openjdk-8-jre-headless \ + graphviz libgraphviz-dev libcairo2-dev pkg-config python3-dev python3.8-venv \ + python3-setuptools liblzma-dev \ + python3-pygraphviz apt-utils + +# RUN poetry config virtualenvs.create false +RUN pip install --upgrade pip +RUN pip install setuptools pygraphviz -# Things that didn't install - make python3-dev - -# Install and setup pyenv -RUN git clone git://github.com/yyuu/pyenv.git .pyenv -RUN git clone https://github.com/yyuu/pyenv-virtualenv.git ~/.pyenv/plugins/pyenv-virtualenv - -ENV HOME / -ENV PYENV_ROOT $HOME/.pyenv -ENV PATH $PYENV_ROOT/shims:$PYENV_ROOT/bin:$PATH - -RUN pyenv install 3.8.0 -RUN pyenv global 3.8.0 - -RUN pip install pygraphviz - -RUN pip install "poetry==$POETRY_VERSION" - -RUN poetry config virtualenvs.create false -ENV PYTHONPATH=${PYTHONPATH}:${PWD} diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index f7f93da..8a8756b 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -5,23 +5,35 @@ "build": { "dockerfile": "Dockerfile", // Update 'VARIANT' to pick an Ubuntu version: focal, bionic - "args": { "VARIANT": "focal" } + "args": { + "VARIANT": "focal" + } }, - // Set *default* container specific settings.json values on container create. - "settings": { - "terminal.integrated.shell.linux": "/bin/bash" - }, - // Add the IDs of extensions you want installed when the container is created. - "extensions": ["ms-python.vscode-pylance", "ms-python.python","njpwerner.autodocstring","bungcip.better-toml", "rkrishnasanka.lfr", "rkrishnasanka.uf"] - + "customizations": { + "vscode": { + "extensions": [ + "ms-python.vscode-pylance", + "ms-python.python", + "njpwerner.autodocstring", + "bungcip.better-toml", + "rkrishnasanka.lfr", + "rkrishnasanka.uf", + "LittleFoxTeam.vscode-python-test-adapter" + ] + } + }, + "features": { + "ghcr.io/devcontainers-contrib/features/black:2": {}, + "ghcr.io/devcontainers-contrib/features/isort:2": {}, + "ghcr.io/devcontainers-contrib/features/poetry:2": {}, + "ghcr.io/devcontainers-contrib/features/pylint:2": {} + }, // Use 'forwardPorts' to make a list of ports inside the container available locally. // "forwardPorts": [], - // Use 'postCreateCommand' to run commands after the container is created. // "postCreateCommand": "uname -a", - // Comment out connect as root instead. More info: https://aka.ms/vscode-remote/containers/non-root. - // "remoteUser": "vscode" -} + "remoteUser": "root" +} \ No newline at end of file diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..4bb443c --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +*.pdf binary \ No newline at end of file diff --git a/.gitignore b/.gitignore index 3ddcae8..334f4fb 100644 --- a/.gitignore +++ b/.gitignore @@ -130,10 +130,11 @@ dmypy.json # Output out/ +output/ .idea/ .antlr/ Verilog2001.g4 *.old -pre_processor_dump.lfr \ No newline at end of file +pre_processor_dump.lfr diff --git a/.gitmodules b/.gitmodules index abdfddb..6908d52 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,12 @@ -[submodule "test"] - path = test - url = https://github.com/CIDARLAB/LFR-TestCases.git +[submodule "pymint"] + path = pymint + url = https://github.com/cidarlab/pymint + branch = master +[submodule "pyparchmint"] + path = pyparchmint + url = https://github.com/cidarlab/pyparchmint + branch = master +[submodule "dafd"] + path = dafd + url = https://github.com/rkrishnasanka/dafd + branch = master diff --git a/.vscode/launch.json b/.vscode/launch.json index abf2e31..a215e90 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -75,7 +75,7 @@ "--outpath", "./out/", "--technology", - "mlsi", + "dropx", "${file}" ], "console": "integratedTerminal" diff --git a/.vscode/settings.json b/.vscode/settings.json index 2fd9fa5..da38bed 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,12 +1,20 @@ { - "python.linting.flake8Enabled": true, + "python.linting.flake8Enabled": false, "python.linting.enabled": true, - "python.linting.flake8Args": ["--max-line-length=88", "--ignore=E203"], + "python.linting.flake8Args": [ + "--max-line-length=88", + "--ignore=E203" + ], "python.formatting.provider": "black", "editor.formatOnSave": true, - "python.linting.pylintEnabled": false, + "python.linting.pylintEnabled": true, "python.formatting.blackArgs": [ "--line-length=88", "--experimental-string-processing" - ] -} + ], + "python.testing.pytestArgs": [ + "tests" + ], + "python.testing.unittestEnabled": false, + "python.testing.pytestEnabled": true, +} \ No newline at end of file diff --git a/dafd b/dafd new file mode 160000 index 0000000..3c60c93 --- /dev/null +++ b/dafd @@ -0,0 +1 @@ +Subproject commit 3c60c9313cec92c37b7e5fc8b23e1f5fb9bf0c4b diff --git a/lfr/antlrgen/lfr/lfrX.interp b/lfr/antlrgen/lfr/lfrX.interp index 9cdae8d..0aa2eaa 100755 --- a/lfr/antlrgen/lfr/lfrX.interp +++ b/lfr/antlrgen/lfr/lfrX.interp @@ -223,4 +223,4 @@ number atn: -[3, 24715, 42794, 33075, 47597, 16764, 15335, 30598, 22884, 3, 77, 595, 4, 2, 9, 2, 4, 3, 9, 3, 4, 4, 9, 4, 4, 5, 9, 5, 4, 6, 9, 6, 4, 7, 9, 7, 4, 8, 9, 8, 4, 9, 9, 9, 4, 10, 9, 10, 4, 11, 9, 11, 4, 12, 9, 12, 4, 13, 9, 13, 4, 14, 9, 14, 4, 15, 9, 15, 4, 16, 9, 16, 4, 17, 9, 17, 4, 18, 9, 18, 4, 19, 9, 19, 4, 20, 9, 20, 4, 21, 9, 21, 4, 22, 9, 22, 4, 23, 9, 23, 4, 24, 9, 24, 4, 25, 9, 25, 4, 26, 9, 26, 4, 27, 9, 27, 4, 28, 9, 28, 4, 29, 9, 29, 4, 30, 9, 30, 4, 31, 9, 31, 4, 32, 9, 32, 4, 33, 9, 33, 4, 34, 9, 34, 4, 35, 9, 35, 4, 36, 9, 36, 4, 37, 9, 37, 4, 38, 9, 38, 4, 39, 9, 39, 4, 40, 9, 40, 4, 41, 9, 41, 4, 42, 9, 42, 4, 43, 9, 43, 4, 44, 9, 44, 4, 45, 9, 45, 4, 46, 9, 46, 4, 47, 9, 47, 4, 48, 9, 48, 4, 49, 9, 49, 4, 50, 9, 50, 4, 51, 9, 51, 4, 52, 9, 52, 4, 53, 9, 53, 4, 54, 9, 54, 4, 55, 9, 55, 4, 56, 9, 56, 4, 57, 9, 57, 4, 58, 9, 58, 4, 59, 9, 59, 4, 60, 9, 60, 4, 61, 9, 61, 4, 62, 9, 62, 4, 63, 9, 63, 4, 64, 9, 64, 4, 65, 9, 65, 4, 66, 9, 66, 3, 2, 6, 2, 134, 10, 2, 13, 2, 14, 2, 135, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 3, 4, 3, 4, 3, 4, 3, 4, 3, 4, 5, 4, 148, 10, 4, 3, 4, 3, 4, 3, 5, 3, 5, 6, 5, 154, 10, 5, 13, 5, 14, 5, 155, 3, 6, 3, 6, 3, 6, 7, 6, 161, 10, 6, 12, 6, 14, 6, 164, 11, 6, 3, 6, 3, 6, 3, 6, 7, 6, 169, 10, 6, 12, 6, 14, 6, 172, 11, 6, 5, 6, 174, 10, 6, 3, 7, 3, 7, 5, 7, 178, 10, 7, 3, 8, 3, 8, 3, 8, 3, 8, 7, 8, 184, 10, 8, 12, 8, 14, 8, 187, 11, 8, 3, 8, 3, 8, 3, 8, 3, 8, 7, 8, 193, 10, 8, 12, 8, 14, 8, 196, 11, 8, 3, 8, 3, 8, 3, 8, 3, 8, 7, 8, 202, 10, 8, 12, 8, 14, 8, 205, 11, 8, 5, 8, 207, 10, 8, 3, 9, 5, 9, 210, 10, 9, 3, 9, 3, 9, 3, 10, 3, 10, 3, 10, 3, 10, 3, 10, 3, 10, 3, 10, 3, 10, 3, 11, 6, 11, 223, 10, 11, 13, 11, 14, 11, 224, 3, 12, 3, 12, 3, 12, 5, 12, 230, 10, 12, 3, 13, 3, 13, 7, 13, 234, 10, 13, 12, 13, 14, 13, 237, 11, 13, 3, 13, 5, 13, 240, 10, 13, 3, 14, 3, 14, 3, 14, 3, 14, 3, 14, 3, 14, 3, 15, 3, 15, 3, 15, 3, 16, 3, 16, 3, 16, 3, 16, 3, 16, 3, 16, 3, 16, 3, 17, 3, 17, 3, 17, 3, 17, 3, 18, 3, 18, 6, 18, 264, 10, 18, 13, 18, 14, 18, 265, 3, 18, 3, 18, 3, 18, 5, 18, 271, 10, 18, 3, 19, 3, 19, 6, 19, 275, 10, 19, 13, 19, 14, 19, 276, 3, 19, 5, 19, 280, 10, 19, 3, 19, 3, 19, 3, 20, 3, 20, 3, 20, 3, 20, 3, 20, 3, 21, 3, 21, 3, 21, 3, 21, 3, 22, 3, 22, 3, 22, 3, 22, 3, 23, 3, 23, 3, 24, 3, 24, 3, 24, 3, 24, 3, 24, 5, 24, 304, 10, 24, 3, 24, 3, 24, 3, 25, 3, 25, 3, 25, 7, 25, 311, 10, 25, 12, 25, 14, 25, 314, 11, 25, 3, 26, 3, 26, 5, 26, 318, 10, 26, 3, 27, 3, 27, 3, 27, 3, 27, 5, 27, 324, 10, 27, 3, 28, 3, 28, 3, 28, 3, 28, 3, 28, 5, 28, 331, 10, 28, 3, 29, 3, 29, 3, 29, 3, 29, 3, 29, 3, 29, 3, 30, 3, 30, 5, 30, 341, 10, 30, 3, 31, 3, 31, 3, 31, 7, 31, 346, 10, 31, 12, 31, 14, 31, 349, 11, 31, 3, 32, 3, 32, 3, 32, 7, 32, 354, 10, 32, 12, 32, 14, 32, 357, 11, 32, 3, 33, 3, 33, 3, 33, 3, 33, 3, 33, 3, 33, 3, 34, 3, 34, 3, 35, 3, 35, 3, 36, 3, 36, 3, 36, 3, 36, 3, 36, 5, 36, 374, 10, 36, 3, 37, 3, 37, 3, 37, 3, 37, 7, 37, 380, 10, 37, 12, 37, 14, 37, 383, 11, 37, 3, 38, 3, 38, 3, 38, 3, 38, 7, 38, 389, 10, 38, 12, 38, 14, 38, 392, 11, 38, 3, 39, 3, 39, 3, 39, 3, 39, 7, 39, 398, 10, 39, 12, 39, 14, 39, 401, 11, 39, 3, 40, 3, 40, 3, 40, 3, 40, 7, 40, 407, 10, 40, 12, 40, 14, 40, 410, 11, 40, 3, 41, 3, 41, 3, 41, 3, 41, 7, 41, 416, 10, 41, 12, 41, 14, 41, 419, 11, 41, 3, 42, 3, 42, 3, 42, 3, 42, 3, 42, 5, 42, 426, 10, 42, 3, 43, 3, 43, 3, 43, 3, 43, 5, 43, 432, 10, 43, 3, 44, 5, 44, 435, 10, 44, 3, 44, 3, 44, 3, 44, 3, 44, 3, 45, 3, 45, 5, 45, 443, 10, 45, 3, 45, 3, 45, 3, 45, 5, 45, 448, 10, 45, 7, 45, 450, 10, 45, 12, 45, 14, 45, 453, 11, 45, 3, 46, 5, 46, 456, 10, 46, 3, 46, 3, 46, 5, 46, 460, 10, 46, 3, 47, 3, 47, 5, 47, 464, 10, 47, 3, 47, 3, 47, 3, 47, 5, 47, 469, 10, 47, 7, 47, 471, 10, 47, 12, 47, 14, 47, 474, 11, 47, 3, 48, 3, 48, 3, 48, 3, 48, 3, 49, 3, 49, 3, 50, 3, 50, 3, 50, 3, 50, 5, 50, 486, 10, 50, 3, 50, 3, 50, 3, 51, 3, 51, 5, 51, 492, 10, 51, 3, 52, 3, 52, 3, 52, 3, 52, 7, 52, 498, 10, 52, 12, 52, 14, 52, 501, 11, 52, 3, 52, 3, 52, 5, 52, 505, 10, 52, 3, 53, 3, 53, 3, 54, 3, 54, 3, 55, 3, 55, 3, 55, 5, 55, 514, 10, 55, 3, 56, 3, 56, 3, 56, 6, 56, 519, 10, 56, 13, 56, 14, 56, 520, 3, 56, 3, 56, 3, 56, 3, 56, 5, 56, 527, 10, 56, 3, 56, 3, 56, 3, 57, 3, 57, 3, 57, 3, 57, 3, 58, 3, 58, 5, 58, 537, 10, 58, 3, 59, 3, 59, 3, 59, 3, 59, 3, 59, 3, 59, 3, 59, 7, 59, 546, 10, 59, 12, 59, 14, 59, 549, 11, 59, 3, 60, 3, 60, 3, 60, 3, 60, 5, 60, 555, 10, 60, 3, 60, 3, 60, 3, 60, 3, 60, 5, 60, 561, 10, 60, 3, 60, 3, 60, 3, 60, 3, 60, 5, 60, 567, 10, 60, 3, 60, 3, 60, 3, 60, 3, 60, 5, 60, 573, 10, 60, 3, 60, 3, 60, 3, 60, 3, 60, 5, 60, 579, 10, 60, 5, 60, 581, 10, 60, 3, 61, 3, 61, 3, 62, 3, 62, 3, 63, 3, 63, 3, 64, 3, 64, 3, 65, 3, 65, 3, 66, 3, 66, 3, 66, 2, 2, 67, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58, 60, 62, 64, 66, 68, 70, 72, 74, 76, 78, 80, 82, 84, 86, 88, 90, 92, 94, 96, 98, 100, 102, 104, 106, 108, 110, 112, 114, 116, 118, 120, 122, 124, 126, 128, 130, 2, 9, 4, 2, 25, 26, 28, 28, 3, 2, 38, 39, 3, 2, 43, 53, 7, 2, 21, 21, 40, 44, 47, 47, 49, 49, 51, 67, 3, 2, 45, 53, 7, 2, 47, 47, 49, 49, 51, 53, 57, 58, 61, 62, 3, 2, 73, 77, 2, 603, 2, 133, 3, 2, 2, 2, 4, 137, 3, 2, 2, 2, 6, 141, 3, 2, 2, 2, 8, 153, 3, 2, 2, 2, 10, 173, 3, 2, 2, 2, 12, 175, 3, 2, 2, 2, 14, 206, 3, 2, 2, 2, 16, 209, 3, 2, 2, 2, 18, 213, 3, 2, 2, 2, 20, 222, 3, 2, 2, 2, 22, 229, 3, 2, 2, 2, 24, 231, 3, 2, 2, 2, 26, 241, 3, 2, 2, 2, 28, 247, 3, 2, 2, 2, 30, 250, 3, 2, 2, 2, 32, 257, 3, 2, 2, 2, 34, 270, 3, 2, 2, 2, 36, 272, 3, 2, 2, 2, 38, 283, 3, 2, 2, 2, 40, 288, 3, 2, 2, 2, 42, 292, 3, 2, 2, 2, 44, 296, 3, 2, 2, 2, 46, 298, 3, 2, 2, 2, 48, 307, 3, 2, 2, 2, 50, 315, 3, 2, 2, 2, 52, 323, 3, 2, 2, 2, 54, 330, 3, 2, 2, 2, 56, 332, 3, 2, 2, 2, 58, 340, 3, 2, 2, 2, 60, 342, 3, 2, 2, 2, 62, 350, 3, 2, 2, 2, 64, 358, 3, 2, 2, 2, 66, 364, 3, 2, 2, 2, 68, 366, 3, 2, 2, 2, 70, 373, 3, 2, 2, 2, 72, 375, 3, 2, 2, 2, 74, 384, 3, 2, 2, 2, 76, 393, 3, 2, 2, 2, 78, 402, 3, 2, 2, 2, 80, 411, 3, 2, 2, 2, 82, 420, 3, 2, 2, 2, 84, 427, 3, 2, 2, 2, 86, 434, 3, 2, 2, 2, 88, 442, 3, 2, 2, 2, 90, 459, 3, 2, 2, 2, 92, 463, 3, 2, 2, 2, 94, 475, 3, 2, 2, 2, 96, 479, 3, 2, 2, 2, 98, 481, 3, 2, 2, 2, 100, 491, 3, 2, 2, 2, 102, 493, 3, 2, 2, 2, 104, 506, 3, 2, 2, 2, 106, 508, 3, 2, 2, 2, 108, 513, 3, 2, 2, 2, 110, 515, 3, 2, 2, 2, 112, 530, 3, 2, 2, 2, 114, 536, 3, 2, 2, 2, 116, 538, 3, 2, 2, 2, 118, 580, 3, 2, 2, 2, 120, 582, 3, 2, 2, 2, 122, 584, 3, 2, 2, 2, 124, 586, 3, 2, 2, 2, 126, 588, 3, 2, 2, 2, 128, 590, 3, 2, 2, 2, 130, 592, 3, 2, 2, 2, 132, 134, 5, 4, 3, 2, 133, 132, 3, 2, 2, 2, 134, 135, 3, 2, 2, 2, 135, 133, 3, 2, 2, 2, 135, 136, 3, 2, 2, 2, 136, 3, 3, 2, 2, 2, 137, 138, 5, 6, 4, 2, 138, 139, 5, 8, 5, 2, 139, 140, 7, 3, 2, 2, 140, 5, 3, 2, 2, 2, 141, 142, 7, 4, 2, 2, 142, 147, 7, 68, 2, 2, 143, 144, 7, 5, 2, 2, 144, 145, 5, 10, 6, 2, 145, 146, 7, 6, 2, 2, 146, 148, 3, 2, 2, 2, 147, 143, 3, 2, 2, 2, 147, 148, 3, 2, 2, 2, 148, 149, 3, 2, 2, 2, 149, 150, 7, 7, 2, 2, 150, 7, 3, 2, 2, 2, 151, 154, 5, 52, 27, 2, 152, 154, 5, 18, 10, 2, 153, 151, 3, 2, 2, 2, 153, 152, 3, 2, 2, 2, 154, 155, 3, 2, 2, 2, 155, 153, 3, 2, 2, 2, 155, 156, 3, 2, 2, 2, 156, 9, 3, 2, 2, 2, 157, 162, 5, 12, 7, 2, 158, 159, 7, 8, 2, 2, 159, 161, 5, 12, 7, 2, 160, 158, 3, 2, 2, 2, 161, 164, 3, 2, 2, 2, 162, 160, 3, 2, 2, 2, 162, 163, 3, 2, 2, 2, 163, 174, 3, 2, 2, 2, 164, 162, 3, 2, 2, 2, 165, 170, 5, 14, 8, 2, 166, 167, 7, 8, 2, 2, 167, 169, 5, 14, 8, 2, 168, 166, 3, 2, 2, 2, 169, 172, 3, 2, 2, 2, 170, 168, 3, 2, 2, 2, 170, 171, 3, 2, 2, 2, 171, 174, 3, 2, 2, 2, 172, 170, 3, 2, 2, 2, 173, 157, 3, 2, 2, 2, 173, 165, 3, 2, 2, 2, 174, 11, 3, 2, 2, 2, 175, 177, 7, 68, 2, 2, 176, 178, 5, 98, 50, 2, 177, 176, 3, 2, 2, 2, 177, 178, 3, 2, 2, 2, 178, 13, 3, 2, 2, 2, 179, 180, 7, 9, 2, 2, 180, 185, 5, 16, 9, 2, 181, 182, 7, 8, 2, 2, 182, 184, 5, 16, 9, 2, 183, 181, 3, 2, 2, 2, 184, 187, 3, 2, 2, 2, 185, 183, 3, 2, 2, 2, 185, 186, 3, 2, 2, 2, 186, 207, 3, 2, 2, 2, 187, 185, 3, 2, 2, 2, 188, 189, 7, 10, 2, 2, 189, 194, 5, 16, 9, 2, 190, 191, 7, 8, 2, 2, 191, 193, 5, 16, 9, 2, 192, 190, 3, 2, 2, 2, 193, 196, 3, 2, 2, 2, 194, 192, 3, 2, 2, 2, 194, 195, 3, 2, 2, 2, 195, 207, 3, 2, 2, 2, 196, 194, 3, 2, 2, 2, 197, 198, 7, 11, 2, 2, 198, 203, 5, 16, 9, 2, 199, 200, 7, 8, 2, 2, 200, 202, 5, 16, 9, 2, 201, 199, 3, 2, 2, 2, 202, 205, 3, 2, 2, 2, 203, 201, 3, 2, 2, 2, 203, 204, 3, 2, 2, 2, 204, 207, 3, 2, 2, 2, 205, 203, 3, 2, 2, 2, 206, 179, 3, 2, 2, 2, 206, 188, 3, 2, 2, 2, 206, 197, 3, 2, 2, 2, 207, 15, 3, 2, 2, 2, 208, 210, 5, 98, 50, 2, 209, 208, 3, 2, 2, 2, 209, 210, 3, 2, 2, 2, 210, 211, 3, 2, 2, 2, 211, 212, 7, 68, 2, 2, 212, 17, 3, 2, 2, 2, 213, 214, 7, 12, 2, 2, 214, 215, 7, 5, 2, 2, 215, 216, 5, 48, 25, 2, 216, 217, 7, 6, 2, 2, 217, 218, 7, 13, 2, 2, 218, 219, 5, 20, 11, 2, 219, 220, 7, 14, 2, 2, 220, 19, 3, 2, 2, 2, 221, 223, 5, 22, 12, 2, 222, 221, 3, 2, 2, 2, 223, 224, 3, 2, 2, 2, 224, 222, 3, 2, 2, 2, 224, 225, 3, 2, 2, 2, 225, 21, 3, 2, 2, 2, 226, 230, 5, 46, 24, 2, 227, 230, 5, 36, 19, 2, 228, 230, 5, 24, 13, 2, 229, 226, 3, 2, 2, 2, 229, 227, 3, 2, 2, 2, 229, 228, 3, 2, 2, 2, 230, 23, 3, 2, 2, 2, 231, 235, 5, 26, 14, 2, 232, 234, 5, 30, 16, 2, 233, 232, 3, 2, 2, 2, 234, 237, 3, 2, 2, 2, 235, 233, 3, 2, 2, 2, 235, 236, 3, 2, 2, 2, 236, 239, 3, 2, 2, 2, 237, 235, 3, 2, 2, 2, 238, 240, 5, 28, 15, 2, 239, 238, 3, 2, 2, 2, 239, 240, 3, 2, 2, 2, 240, 25, 3, 2, 2, 2, 241, 242, 7, 15, 2, 2, 242, 243, 7, 5, 2, 2, 243, 244, 5, 32, 17, 2, 244, 245, 7, 6, 2, 2, 245, 246, 5, 34, 18, 2, 246, 27, 3, 2, 2, 2, 247, 248, 7, 16, 2, 2, 248, 249, 5, 34, 18, 2, 249, 29, 3, 2, 2, 2, 250, 251, 7, 16, 2, 2, 251, 252, 7, 15, 2, 2, 252, 253, 7, 5, 2, 2, 253, 254, 5, 32, 17, 2, 254, 255, 7, 6, 2, 2, 255, 256, 5, 34, 18, 2, 256, 31, 3, 2, 2, 2, 257, 258, 5, 104, 53, 2, 258, 259, 5, 128, 65, 2, 259, 260, 5, 44, 23, 2, 260, 33, 3, 2, 2, 2, 261, 263, 7, 13, 2, 2, 262, 264, 5, 46, 24, 2, 263, 262, 3, 2, 2, 2, 264, 265, 3, 2, 2, 2, 265, 263, 3, 2, 2, 2, 265, 266, 3, 2, 2, 2, 266, 267, 3, 2, 2, 2, 267, 268, 7, 14, 2, 2, 268, 271, 3, 2, 2, 2, 269, 271, 5, 46, 24, 2, 270, 261, 3, 2, 2, 2, 270, 269, 3, 2, 2, 2, 271, 35, 3, 2, 2, 2, 272, 274, 5, 38, 20, 2, 273, 275, 5, 40, 21, 2, 274, 273, 3, 2, 2, 2, 275, 276, 3, 2, 2, 2, 276, 274, 3, 2, 2, 2, 276, 277, 3, 2, 2, 2, 277, 279, 3, 2, 2, 2, 278, 280, 5, 42, 22, 2, 279, 278, 3, 2, 2, 2, 279, 280, 3, 2, 2, 2, 280, 281, 3, 2, 2, 2, 281, 282, 7, 17, 2, 2, 282, 37, 3, 2, 2, 2, 283, 284, 7, 18, 2, 2, 284, 285, 7, 5, 2, 2, 285, 286, 5, 104, 53, 2, 286, 287, 7, 6, 2, 2, 287, 39, 3, 2, 2, 2, 288, 289, 5, 44, 23, 2, 289, 290, 7, 19, 2, 2, 290, 291, 5, 34, 18, 2, 291, 41, 3, 2, 2, 2, 292, 293, 7, 20, 2, 2, 293, 294, 7, 19, 2, 2, 294, 295, 5, 34, 18, 2, 295, 43, 3, 2, 2, 2, 296, 297, 5, 130, 66, 2, 297, 45, 3, 2, 2, 2, 298, 299, 5, 104, 53, 2, 299, 303, 7, 21, 2, 2, 300, 304, 5, 130, 66, 2, 301, 304, 5, 100, 51, 2, 302, 304, 5, 88, 45, 2, 303, 300, 3, 2, 2, 2, 303, 301, 3, 2, 2, 2, 303, 302, 3, 2, 2, 2, 304, 305, 3, 2, 2, 2, 305, 306, 7, 7, 2, 2, 306, 47, 3, 2, 2, 2, 307, 312, 5, 50, 26, 2, 308, 309, 7, 8, 2, 2, 309, 311, 5, 50, 26, 2, 310, 308, 3, 2, 2, 2, 311, 314, 3, 2, 2, 2, 312, 310, 3, 2, 2, 2, 312, 313, 3, 2, 2, 2, 313, 49, 3, 2, 2, 2, 314, 312, 3, 2, 2, 2, 315, 317, 7, 68, 2, 2, 316, 318, 5, 98, 50, 2, 317, 316, 3, 2, 2, 2, 317, 318, 3, 2, 2, 2, 318, 51, 3, 2, 2, 2, 319, 320, 5, 54, 28, 2, 320, 321, 7, 7, 2, 2, 321, 324, 3, 2, 2, 2, 322, 324, 5, 108, 55, 2, 323, 319, 3, 2, 2, 2, 323, 322, 3, 2, 2, 2, 324, 53, 3, 2, 2, 2, 325, 331, 5, 106, 54, 2, 326, 331, 5, 82, 42, 2, 327, 331, 5, 70, 36, 2, 328, 331, 5, 84, 43, 2, 329, 331, 5, 56, 29, 2, 330, 325, 3, 2, 2, 2, 330, 326, 3, 2, 2, 2, 330, 327, 3, 2, 2, 2, 330, 328, 3, 2, 2, 2, 330, 329, 3, 2, 2, 2, 331, 55, 3, 2, 2, 2, 332, 333, 5, 68, 35, 2, 333, 334, 5, 66, 34, 2, 334, 335, 7, 5, 2, 2, 335, 336, 5, 58, 30, 2, 336, 337, 7, 6, 2, 2, 337, 57, 3, 2, 2, 2, 338, 341, 5, 60, 31, 2, 339, 341, 5, 62, 32, 2, 340, 338, 3, 2, 2, 2, 340, 339, 3, 2, 2, 2, 341, 59, 3, 2, 2, 2, 342, 347, 5, 12, 7, 2, 343, 344, 7, 8, 2, 2, 344, 346, 5, 12, 7, 2, 345, 343, 3, 2, 2, 2, 346, 349, 3, 2, 2, 2, 347, 345, 3, 2, 2, 2, 347, 348, 3, 2, 2, 2, 348, 61, 3, 2, 2, 2, 349, 347, 3, 2, 2, 2, 350, 355, 5, 64, 33, 2, 351, 352, 7, 8, 2, 2, 352, 354, 5, 64, 33, 2, 353, 351, 3, 2, 2, 2, 354, 357, 3, 2, 2, 2, 355, 353, 3, 2, 2, 2, 355, 356, 3, 2, 2, 2, 356, 63, 3, 2, 2, 2, 357, 355, 3, 2, 2, 2, 358, 359, 7, 22, 2, 2, 359, 360, 7, 68, 2, 2, 360, 361, 7, 5, 2, 2, 361, 362, 5, 100, 51, 2, 362, 363, 7, 6, 2, 2, 363, 65, 3, 2, 2, 2, 364, 365, 7, 68, 2, 2, 365, 67, 3, 2, 2, 2, 366, 367, 7, 68, 2, 2, 367, 69, 3, 2, 2, 2, 368, 374, 5, 74, 38, 2, 369, 374, 5, 76, 39, 2, 370, 374, 5, 80, 41, 2, 371, 374, 5, 72, 37, 2, 372, 374, 5, 78, 40, 2, 373, 368, 3, 2, 2, 2, 373, 369, 3, 2, 2, 2, 373, 370, 3, 2, 2, 2, 373, 371, 3, 2, 2, 2, 373, 372, 3, 2, 2, 2, 374, 71, 3, 2, 2, 2, 375, 376, 7, 23, 2, 2, 376, 381, 5, 16, 9, 2, 377, 378, 7, 8, 2, 2, 378, 380, 5, 16, 9, 2, 379, 377, 3, 2, 2, 2, 380, 383, 3, 2, 2, 2, 381, 379, 3, 2, 2, 2, 381, 382, 3, 2, 2, 2, 382, 73, 3, 2, 2, 2, 383, 381, 3, 2, 2, 2, 384, 385, 7, 24, 2, 2, 385, 390, 5, 16, 9, 2, 386, 387, 7, 8, 2, 2, 387, 389, 5, 16, 9, 2, 388, 386, 3, 2, 2, 2, 389, 392, 3, 2, 2, 2, 390, 388, 3, 2, 2, 2, 390, 391, 3, 2, 2, 2, 391, 75, 3, 2, 2, 2, 392, 390, 3, 2, 2, 2, 393, 394, 7, 25, 2, 2, 394, 399, 5, 16, 9, 2, 395, 396, 7, 8, 2, 2, 396, 398, 5, 16, 9, 2, 397, 395, 3, 2, 2, 2, 398, 401, 3, 2, 2, 2, 399, 397, 3, 2, 2, 2, 399, 400, 3, 2, 2, 2, 400, 77, 3, 2, 2, 2, 401, 399, 3, 2, 2, 2, 402, 403, 7, 26, 2, 2, 403, 408, 5, 16, 9, 2, 404, 405, 7, 8, 2, 2, 405, 407, 5, 16, 9, 2, 406, 404, 3, 2, 2, 2, 407, 410, 3, 2, 2, 2, 408, 406, 3, 2, 2, 2, 408, 409, 3, 2, 2, 2, 409, 79, 3, 2, 2, 2, 410, 408, 3, 2, 2, 2, 411, 412, 7, 27, 2, 2, 412, 417, 5, 84, 43, 2, 413, 414, 7, 8, 2, 2, 414, 416, 5, 84, 43, 2, 415, 413, 3, 2, 2, 2, 416, 419, 3, 2, 2, 2, 417, 415, 3, 2, 2, 2, 417, 418, 3, 2, 2, 2, 418, 81, 3, 2, 2, 2, 419, 417, 3, 2, 2, 2, 420, 421, 7, 28, 2, 2, 421, 422, 5, 104, 53, 2, 422, 425, 7, 29, 2, 2, 423, 426, 5, 86, 44, 2, 424, 426, 5, 88, 45, 2, 425, 423, 3, 2, 2, 2, 425, 424, 3, 2, 2, 2, 426, 83, 3, 2, 2, 2, 427, 428, 7, 68, 2, 2, 428, 431, 7, 29, 2, 2, 429, 432, 5, 86, 44, 2, 430, 432, 5, 88, 45, 2, 431, 429, 3, 2, 2, 2, 431, 430, 3, 2, 2, 2, 432, 85, 3, 2, 2, 2, 433, 435, 5, 122, 62, 2, 434, 433, 3, 2, 2, 2, 434, 435, 3, 2, 2, 2, 435, 436, 3, 2, 2, 2, 436, 437, 7, 5, 2, 2, 437, 438, 5, 88, 45, 2, 438, 439, 7, 6, 2, 2, 439, 87, 3, 2, 2, 2, 440, 443, 5, 86, 44, 2, 441, 443, 5, 90, 46, 2, 442, 440, 3, 2, 2, 2, 442, 441, 3, 2, 2, 2, 443, 451, 3, 2, 2, 2, 444, 447, 5, 124, 63, 2, 445, 448, 5, 86, 44, 2, 446, 448, 5, 90, 46, 2, 447, 445, 3, 2, 2, 2, 447, 446, 3, 2, 2, 2, 448, 450, 3, 2, 2, 2, 449, 444, 3, 2, 2, 2, 450, 453, 3, 2, 2, 2, 451, 449, 3, 2, 2, 2, 451, 452, 3, 2, 2, 2, 452, 89, 3, 2, 2, 2, 453, 451, 3, 2, 2, 2, 454, 456, 5, 122, 62, 2, 455, 454, 3, 2, 2, 2, 455, 456, 3, 2, 2, 2, 456, 457, 3, 2, 2, 2, 457, 460, 5, 100, 51, 2, 458, 460, 5, 130, 66, 2, 459, 455, 3, 2, 2, 2, 459, 458, 3, 2, 2, 2, 460, 91, 3, 2, 2, 2, 461, 464, 5, 86, 44, 2, 462, 464, 5, 90, 46, 2, 463, 461, 3, 2, 2, 2, 463, 462, 3, 2, 2, 2, 464, 472, 3, 2, 2, 2, 465, 468, 5, 124, 63, 2, 466, 469, 5, 86, 44, 2, 467, 469, 5, 90, 46, 2, 468, 466, 3, 2, 2, 2, 468, 467, 3, 2, 2, 2, 469, 471, 3, 2, 2, 2, 470, 465, 3, 2, 2, 2, 471, 474, 3, 2, 2, 2, 472, 470, 3, 2, 2, 2, 472, 473, 3, 2, 2, 2, 473, 93, 3, 2, 2, 2, 474, 472, 3, 2, 2, 2, 475, 476, 5, 92, 47, 2, 476, 477, 5, 128, 65, 2, 477, 478, 5, 96, 49, 2, 478, 95, 3, 2, 2, 2, 479, 480, 5, 130, 66, 2, 480, 97, 3, 2, 2, 2, 481, 482, 7, 30, 2, 2, 482, 485, 7, 74, 2, 2, 483, 484, 7, 19, 2, 2, 484, 486, 7, 74, 2, 2, 485, 483, 3, 2, 2, 2, 485, 486, 3, 2, 2, 2, 486, 487, 3, 2, 2, 2, 487, 488, 7, 31, 2, 2, 488, 99, 3, 2, 2, 2, 489, 492, 5, 12, 7, 2, 490, 492, 5, 102, 52, 2, 491, 489, 3, 2, 2, 2, 491, 490, 3, 2, 2, 2, 492, 101, 3, 2, 2, 2, 493, 494, 7, 32, 2, 2, 494, 499, 5, 12, 7, 2, 495, 496, 7, 8, 2, 2, 496, 498, 5, 12, 7, 2, 497, 495, 3, 2, 2, 2, 498, 501, 3, 2, 2, 2, 499, 497, 3, 2, 2, 2, 499, 500, 3, 2, 2, 2, 500, 502, 3, 2, 2, 2, 501, 499, 3, 2, 2, 2, 502, 504, 7, 33, 2, 2, 503, 505, 5, 98, 50, 2, 504, 503, 3, 2, 2, 2, 504, 505, 3, 2, 2, 2, 505, 103, 3, 2, 2, 2, 506, 507, 5, 100, 51, 2, 507, 105, 3, 2, 2, 2, 508, 509, 5, 14, 8, 2, 509, 107, 3, 2, 2, 2, 510, 514, 5, 116, 59, 2, 511, 514, 5, 110, 56, 2, 512, 514, 5, 112, 57, 2, 513, 510, 3, 2, 2, 2, 513, 511, 3, 2, 2, 2, 513, 512, 3, 2, 2, 2, 514, 109, 3, 2, 2, 2, 515, 516, 7, 34, 2, 2, 516, 518, 7, 35, 2, 2, 517, 519, 7, 68, 2, 2, 518, 517, 3, 2, 2, 2, 519, 520, 3, 2, 2, 2, 520, 518, 3, 2, 2, 2, 520, 521, 3, 2, 2, 2, 521, 522, 3, 2, 2, 2, 522, 523, 7, 35, 2, 2, 523, 526, 7, 35, 2, 2, 524, 527, 5, 114, 58, 2, 525, 527, 9, 2, 2, 2, 526, 524, 3, 2, 2, 2, 526, 525, 3, 2, 2, 2, 527, 528, 3, 2, 2, 2, 528, 529, 7, 35, 2, 2, 529, 111, 3, 2, 2, 2, 530, 531, 7, 36, 2, 2, 531, 532, 7, 68, 2, 2, 532, 533, 7, 68, 2, 2, 533, 113, 3, 2, 2, 2, 534, 537, 5, 124, 63, 2, 535, 537, 5, 122, 62, 2, 536, 534, 3, 2, 2, 2, 536, 535, 3, 2, 2, 2, 537, 115, 3, 2, 2, 2, 538, 539, 7, 37, 2, 2, 539, 540, 7, 35, 2, 2, 540, 541, 5, 114, 58, 2, 541, 542, 7, 35, 2, 2, 542, 547, 5, 118, 60, 2, 543, 544, 9, 3, 2, 2, 544, 546, 5, 118, 60, 2, 545, 543, 3, 2, 2, 2, 546, 549, 3, 2, 2, 2, 547, 545, 3, 2, 2, 2, 547, 548, 3, 2, 2, 2, 548, 117, 3, 2, 2, 2, 549, 547, 3, 2, 2, 2, 550, 551, 7, 68, 2, 2, 551, 552, 7, 29, 2, 2, 552, 554, 5, 130, 66, 2, 553, 555, 5, 120, 61, 2, 554, 553, 3, 2, 2, 2, 554, 555, 3, 2, 2, 2, 555, 581, 3, 2, 2, 2, 556, 557, 7, 68, 2, 2, 557, 558, 7, 40, 2, 2, 558, 560, 5, 130, 66, 2, 559, 561, 5, 120, 61, 2, 560, 559, 3, 2, 2, 2, 560, 561, 3, 2, 2, 2, 561, 581, 3, 2, 2, 2, 562, 563, 7, 68, 2, 2, 563, 564, 7, 41, 2, 2, 564, 566, 5, 130, 66, 2, 565, 567, 5, 120, 61, 2, 566, 565, 3, 2, 2, 2, 566, 567, 3, 2, 2, 2, 567, 581, 3, 2, 2, 2, 568, 569, 7, 68, 2, 2, 569, 570, 7, 42, 2, 2, 570, 572, 5, 130, 66, 2, 571, 573, 5, 120, 61, 2, 572, 571, 3, 2, 2, 2, 572, 573, 3, 2, 2, 2, 573, 581, 3, 2, 2, 2, 574, 575, 7, 68, 2, 2, 575, 576, 7, 21, 2, 2, 576, 578, 5, 130, 66, 2, 577, 579, 5, 120, 61, 2, 578, 577, 3, 2, 2, 2, 578, 579, 3, 2, 2, 2, 579, 581, 3, 2, 2, 2, 580, 550, 3, 2, 2, 2, 580, 556, 3, 2, 2, 2, 580, 562, 3, 2, 2, 2, 580, 568, 3, 2, 2, 2, 580, 574, 3, 2, 2, 2, 581, 119, 3, 2, 2, 2, 582, 583, 7, 68, 2, 2, 583, 121, 3, 2, 2, 2, 584, 585, 9, 4, 2, 2, 585, 123, 3, 2, 2, 2, 586, 587, 9, 5, 2, 2, 587, 125, 3, 2, 2, 2, 588, 589, 9, 6, 2, 2, 589, 127, 3, 2, 2, 2, 590, 591, 9, 7, 2, 2, 591, 129, 3, 2, 2, 2, 592, 593, 9, 8, 2, 2, 593, 131, 3, 2, 2, 2, 63, 135, 147, 153, 155, 162, 170, 173, 177, 185, 194, 203, 206, 209, 224, 229, 235, 239, 265, 270, 276, 279, 303, 312, 317, 323, 330, 340, 347, 355, 373, 381, 390, 399, 408, 417, 425, 431, 434, 442, 447, 451, 455, 459, 463, 468, 472, 485, 491, 499, 504, 513, 520, 526, 536, 547, 554, 560, 566, 572, 578, 580] \ No newline at end of file +[4, 1, 75, 593, 2, 0, 7, 0, 2, 1, 7, 1, 2, 2, 7, 2, 2, 3, 7, 3, 2, 4, 7, 4, 2, 5, 7, 5, 2, 6, 7, 6, 2, 7, 7, 7, 2, 8, 7, 8, 2, 9, 7, 9, 2, 10, 7, 10, 2, 11, 7, 11, 2, 12, 7, 12, 2, 13, 7, 13, 2, 14, 7, 14, 2, 15, 7, 15, 2, 16, 7, 16, 2, 17, 7, 17, 2, 18, 7, 18, 2, 19, 7, 19, 2, 20, 7, 20, 2, 21, 7, 21, 2, 22, 7, 22, 2, 23, 7, 23, 2, 24, 7, 24, 2, 25, 7, 25, 2, 26, 7, 26, 2, 27, 7, 27, 2, 28, 7, 28, 2, 29, 7, 29, 2, 30, 7, 30, 2, 31, 7, 31, 2, 32, 7, 32, 2, 33, 7, 33, 2, 34, 7, 34, 2, 35, 7, 35, 2, 36, 7, 36, 2, 37, 7, 37, 2, 38, 7, 38, 2, 39, 7, 39, 2, 40, 7, 40, 2, 41, 7, 41, 2, 42, 7, 42, 2, 43, 7, 43, 2, 44, 7, 44, 2, 45, 7, 45, 2, 46, 7, 46, 2, 47, 7, 47, 2, 48, 7, 48, 2, 49, 7, 49, 2, 50, 7, 50, 2, 51, 7, 51, 2, 52, 7, 52, 2, 53, 7, 53, 2, 54, 7, 54, 2, 55, 7, 55, 2, 56, 7, 56, 2, 57, 7, 57, 2, 58, 7, 58, 2, 59, 7, 59, 2, 60, 7, 60, 2, 61, 7, 61, 2, 62, 7, 62, 2, 63, 7, 63, 2, 64, 7, 64, 1, 0, 4, 0, 132, 8, 0, 11, 0, 12, 0, 133, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 3, 2, 146, 8, 2, 1, 2, 1, 2, 1, 3, 1, 3, 4, 3, 152, 8, 3, 11, 3, 12, 3, 153, 1, 4, 1, 4, 1, 4, 5, 4, 159, 8, 4, 10, 4, 12, 4, 162, 9, 4, 1, 4, 1, 4, 1, 4, 5, 4, 167, 8, 4, 10, 4, 12, 4, 170, 9, 4, 3, 4, 172, 8, 4, 1, 5, 1, 5, 3, 5, 176, 8, 5, 1, 6, 1, 6, 1, 6, 1, 6, 5, 6, 182, 8, 6, 10, 6, 12, 6, 185, 9, 6, 1, 6, 1, 6, 1, 6, 1, 6, 5, 6, 191, 8, 6, 10, 6, 12, 6, 194, 9, 6, 1, 6, 1, 6, 1, 6, 1, 6, 5, 6, 200, 8, 6, 10, 6, 12, 6, 203, 9, 6, 3, 6, 205, 8, 6, 1, 7, 3, 7, 208, 8, 7, 1, 7, 1, 7, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 9, 4, 9, 221, 8, 9, 11, 9, 12, 9, 222, 1, 10, 1, 10, 1, 10, 3, 10, 228, 8, 10, 1, 11, 1, 11, 5, 11, 232, 8, 11, 10, 11, 12, 11, 235, 9, 11, 1, 11, 3, 11, 238, 8, 11, 1, 12, 1, 12, 1, 12, 1, 12, 1, 12, 1, 12, 1, 13, 1, 13, 1, 13, 1, 14, 1, 14, 1, 14, 1, 14, 1, 14, 1, 14, 1, 14, 1, 15, 1, 15, 1, 15, 1, 15, 1, 16, 1, 16, 4, 16, 262, 8, 16, 11, 16, 12, 16, 263, 1, 16, 1, 16, 1, 16, 3, 16, 269, 8, 16, 1, 17, 1, 17, 4, 17, 273, 8, 17, 11, 17, 12, 17, 274, 1, 17, 3, 17, 278, 8, 17, 1, 17, 1, 17, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 19, 1, 19, 1, 19, 1, 19, 1, 20, 1, 20, 1, 20, 1, 20, 1, 21, 1, 21, 1, 22, 1, 22, 1, 22, 1, 22, 1, 22, 3, 22, 302, 8, 22, 1, 22, 1, 22, 1, 23, 1, 23, 1, 23, 5, 23, 309, 8, 23, 10, 23, 12, 23, 312, 9, 23, 1, 24, 1, 24, 3, 24, 316, 8, 24, 1, 25, 1, 25, 1, 25, 1, 25, 3, 25, 322, 8, 25, 1, 26, 1, 26, 1, 26, 1, 26, 1, 26, 3, 26, 329, 8, 26, 1, 27, 1, 27, 1, 27, 1, 27, 1, 27, 1, 27, 1, 28, 1, 28, 3, 28, 339, 8, 28, 1, 29, 1, 29, 1, 29, 5, 29, 344, 8, 29, 10, 29, 12, 29, 347, 9, 29, 1, 30, 1, 30, 1, 30, 5, 30, 352, 8, 30, 10, 30, 12, 30, 355, 9, 30, 1, 31, 1, 31, 1, 31, 1, 31, 1, 31, 1, 31, 1, 32, 1, 32, 1, 33, 1, 33, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 3, 34, 372, 8, 34, 1, 35, 1, 35, 1, 35, 1, 35, 5, 35, 378, 8, 35, 10, 35, 12, 35, 381, 9, 35, 1, 36, 1, 36, 1, 36, 1, 36, 5, 36, 387, 8, 36, 10, 36, 12, 36, 390, 9, 36, 1, 37, 1, 37, 1, 37, 1, 37, 5, 37, 396, 8, 37, 10, 37, 12, 37, 399, 9, 37, 1, 38, 1, 38, 1, 38, 1, 38, 5, 38, 405, 8, 38, 10, 38, 12, 38, 408, 9, 38, 1, 39, 1, 39, 1, 39, 1, 39, 5, 39, 414, 8, 39, 10, 39, 12, 39, 417, 9, 39, 1, 40, 1, 40, 1, 40, 1, 40, 1, 40, 3, 40, 424, 8, 40, 1, 41, 1, 41, 1, 41, 1, 41, 3, 41, 430, 8, 41, 1, 42, 3, 42, 433, 8, 42, 1, 42, 1, 42, 1, 42, 1, 42, 1, 43, 1, 43, 3, 43, 441, 8, 43, 1, 43, 1, 43, 1, 43, 3, 43, 446, 8, 43, 5, 43, 448, 8, 43, 10, 43, 12, 43, 451, 9, 43, 1, 44, 3, 44, 454, 8, 44, 1, 44, 1, 44, 3, 44, 458, 8, 44, 1, 45, 1, 45, 3, 45, 462, 8, 45, 1, 45, 1, 45, 1, 45, 3, 45, 467, 8, 45, 5, 45, 469, 8, 45, 10, 45, 12, 45, 472, 9, 45, 1, 46, 1, 46, 1, 46, 1, 46, 1, 47, 1, 47, 1, 48, 1, 48, 1, 48, 1, 48, 3, 48, 484, 8, 48, 1, 48, 1, 48, 1, 49, 1, 49, 3, 49, 490, 8, 49, 1, 50, 1, 50, 1, 50, 1, 50, 5, 50, 496, 8, 50, 10, 50, 12, 50, 499, 9, 50, 1, 50, 1, 50, 3, 50, 503, 8, 50, 1, 51, 1, 51, 1, 52, 1, 52, 1, 53, 1, 53, 1, 53, 3, 53, 512, 8, 53, 1, 54, 1, 54, 1, 54, 4, 54, 517, 8, 54, 11, 54, 12, 54, 518, 1, 54, 1, 54, 1, 54, 1, 54, 3, 54, 525, 8, 54, 1, 54, 1, 54, 1, 55, 1, 55, 1, 55, 1, 55, 1, 56, 1, 56, 3, 56, 535, 8, 56, 1, 57, 1, 57, 1, 57, 1, 57, 1, 57, 1, 57, 1, 57, 5, 57, 544, 8, 57, 10, 57, 12, 57, 547, 9, 57, 1, 58, 1, 58, 1, 58, 1, 58, 3, 58, 553, 8, 58, 1, 58, 1, 58, 1, 58, 1, 58, 3, 58, 559, 8, 58, 1, 58, 1, 58, 1, 58, 1, 58, 3, 58, 565, 8, 58, 1, 58, 1, 58, 1, 58, 1, 58, 3, 58, 571, 8, 58, 1, 58, 1, 58, 1, 58, 1, 58, 3, 58, 577, 8, 58, 3, 58, 579, 8, 58, 1, 59, 1, 59, 1, 60, 1, 60, 1, 61, 1, 61, 1, 62, 1, 62, 1, 63, 1, 63, 1, 64, 1, 64, 1, 64, 0, 0, 65, 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58, 60, 62, 64, 66, 68, 70, 72, 74, 76, 78, 80, 82, 84, 86, 88, 90, 92, 94, 96, 98, 100, 102, 104, 106, 108, 110, 112, 114, 116, 118, 120, 122, 124, 126, 128, 0, 7, 2, 0, 23, 24, 26, 26, 1, 0, 36, 37, 1, 0, 41, 51, 5, 0, 19, 19, 38, 42, 45, 45, 47, 47, 49, 65, 1, 0, 43, 51, 5, 0, 45, 45, 47, 47, 49, 51, 55, 56, 59, 60, 1, 0, 71, 75, 601, 0, 131, 1, 0, 0, 0, 2, 135, 1, 0, 0, 0, 4, 139, 1, 0, 0, 0, 6, 151, 1, 0, 0, 0, 8, 171, 1, 0, 0, 0, 10, 173, 1, 0, 0, 0, 12, 204, 1, 0, 0, 0, 14, 207, 1, 0, 0, 0, 16, 211, 1, 0, 0, 0, 18, 220, 1, 0, 0, 0, 20, 227, 1, 0, 0, 0, 22, 229, 1, 0, 0, 0, 24, 239, 1, 0, 0, 0, 26, 245, 1, 0, 0, 0, 28, 248, 1, 0, 0, 0, 30, 255, 1, 0, 0, 0, 32, 268, 1, 0, 0, 0, 34, 270, 1, 0, 0, 0, 36, 281, 1, 0, 0, 0, 38, 286, 1, 0, 0, 0, 40, 290, 1, 0, 0, 0, 42, 294, 1, 0, 0, 0, 44, 296, 1, 0, 0, 0, 46, 305, 1, 0, 0, 0, 48, 313, 1, 0, 0, 0, 50, 321, 1, 0, 0, 0, 52, 328, 1, 0, 0, 0, 54, 330, 1, 0, 0, 0, 56, 338, 1, 0, 0, 0, 58, 340, 1, 0, 0, 0, 60, 348, 1, 0, 0, 0, 62, 356, 1, 0, 0, 0, 64, 362, 1, 0, 0, 0, 66, 364, 1, 0, 0, 0, 68, 371, 1, 0, 0, 0, 70, 373, 1, 0, 0, 0, 72, 382, 1, 0, 0, 0, 74, 391, 1, 0, 0, 0, 76, 400, 1, 0, 0, 0, 78, 409, 1, 0, 0, 0, 80, 418, 1, 0, 0, 0, 82, 425, 1, 0, 0, 0, 84, 432, 1, 0, 0, 0, 86, 440, 1, 0, 0, 0, 88, 457, 1, 0, 0, 0, 90, 461, 1, 0, 0, 0, 92, 473, 1, 0, 0, 0, 94, 477, 1, 0, 0, 0, 96, 479, 1, 0, 0, 0, 98, 489, 1, 0, 0, 0, 100, 491, 1, 0, 0, 0, 102, 504, 1, 0, 0, 0, 104, 506, 1, 0, 0, 0, 106, 511, 1, 0, 0, 0, 108, 513, 1, 0, 0, 0, 110, 528, 1, 0, 0, 0, 112, 534, 1, 0, 0, 0, 114, 536, 1, 0, 0, 0, 116, 578, 1, 0, 0, 0, 118, 580, 1, 0, 0, 0, 120, 582, 1, 0, 0, 0, 122, 584, 1, 0, 0, 0, 124, 586, 1, 0, 0, 0, 126, 588, 1, 0, 0, 0, 128, 590, 1, 0, 0, 0, 130, 132, 3, 2, 1, 0, 131, 130, 1, 0, 0, 0, 132, 133, 1, 0, 0, 0, 133, 131, 1, 0, 0, 0, 133, 134, 1, 0, 0, 0, 134, 1, 1, 0, 0, 0, 135, 136, 3, 4, 2, 0, 136, 137, 3, 6, 3, 0, 137, 138, 5, 1, 0, 0, 138, 3, 1, 0, 0, 0, 139, 140, 5, 2, 0, 0, 140, 145, 5, 66, 0, 0, 141, 142, 5, 3, 0, 0, 142, 143, 3, 8, 4, 0, 143, 144, 5, 4, 0, 0, 144, 146, 1, 0, 0, 0, 145, 141, 1, 0, 0, 0, 145, 146, 1, 0, 0, 0, 146, 147, 1, 0, 0, 0, 147, 148, 5, 5, 0, 0, 148, 5, 1, 0, 0, 0, 149, 152, 3, 50, 25, 0, 150, 152, 3, 16, 8, 0, 151, 149, 1, 0, 0, 0, 151, 150, 1, 0, 0, 0, 152, 153, 1, 0, 0, 0, 153, 151, 1, 0, 0, 0, 153, 154, 1, 0, 0, 0, 154, 7, 1, 0, 0, 0, 155, 160, 3, 10, 5, 0, 156, 157, 5, 6, 0, 0, 157, 159, 3, 10, 5, 0, 158, 156, 1, 0, 0, 0, 159, 162, 1, 0, 0, 0, 160, 158, 1, 0, 0, 0, 160, 161, 1, 0, 0, 0, 161, 172, 1, 0, 0, 0, 162, 160, 1, 0, 0, 0, 163, 168, 3, 12, 6, 0, 164, 165, 5, 6, 0, 0, 165, 167, 3, 12, 6, 0, 166, 164, 1, 0, 0, 0, 167, 170, 1, 0, 0, 0, 168, 166, 1, 0, 0, 0, 168, 169, 1, 0, 0, 0, 169, 172, 1, 0, 0, 0, 170, 168, 1, 0, 0, 0, 171, 155, 1, 0, 0, 0, 171, 163, 1, 0, 0, 0, 172, 9, 1, 0, 0, 0, 173, 175, 5, 66, 0, 0, 174, 176, 3, 96, 48, 0, 175, 174, 1, 0, 0, 0, 175, 176, 1, 0, 0, 0, 176, 11, 1, 0, 0, 0, 177, 178, 5, 7, 0, 0, 178, 183, 3, 14, 7, 0, 179, 180, 5, 6, 0, 0, 180, 182, 3, 14, 7, 0, 181, 179, 1, 0, 0, 0, 182, 185, 1, 0, 0, 0, 183, 181, 1, 0, 0, 0, 183, 184, 1, 0, 0, 0, 184, 205, 1, 0, 0, 0, 185, 183, 1, 0, 0, 0, 186, 187, 5, 8, 0, 0, 187, 192, 3, 14, 7, 0, 188, 189, 5, 6, 0, 0, 189, 191, 3, 14, 7, 0, 190, 188, 1, 0, 0, 0, 191, 194, 1, 0, 0, 0, 192, 190, 1, 0, 0, 0, 192, 193, 1, 0, 0, 0, 193, 205, 1, 0, 0, 0, 194, 192, 1, 0, 0, 0, 195, 196, 5, 9, 0, 0, 196, 201, 3, 14, 7, 0, 197, 198, 5, 6, 0, 0, 198, 200, 3, 14, 7, 0, 199, 197, 1, 0, 0, 0, 200, 203, 1, 0, 0, 0, 201, 199, 1, 0, 0, 0, 201, 202, 1, 0, 0, 0, 202, 205, 1, 0, 0, 0, 203, 201, 1, 0, 0, 0, 204, 177, 1, 0, 0, 0, 204, 186, 1, 0, 0, 0, 204, 195, 1, 0, 0, 0, 205, 13, 1, 0, 0, 0, 206, 208, 3, 96, 48, 0, 207, 206, 1, 0, 0, 0, 207, 208, 1, 0, 0, 0, 208, 209, 1, 0, 0, 0, 209, 210, 5, 66, 0, 0, 210, 15, 1, 0, 0, 0, 211, 212, 5, 10, 0, 0, 212, 213, 5, 3, 0, 0, 213, 214, 3, 46, 23, 0, 214, 215, 5, 4, 0, 0, 215, 216, 5, 11, 0, 0, 216, 217, 3, 18, 9, 0, 217, 218, 5, 12, 0, 0, 218, 17, 1, 0, 0, 0, 219, 221, 3, 20, 10, 0, 220, 219, 1, 0, 0, 0, 221, 222, 1, 0, 0, 0, 222, 220, 1, 0, 0, 0, 222, 223, 1, 0, 0, 0, 223, 19, 1, 0, 0, 0, 224, 228, 3, 44, 22, 0, 225, 228, 3, 34, 17, 0, 226, 228, 3, 22, 11, 0, 227, 224, 1, 0, 0, 0, 227, 225, 1, 0, 0, 0, 227, 226, 1, 0, 0, 0, 228, 21, 1, 0, 0, 0, 229, 233, 3, 24, 12, 0, 230, 232, 3, 28, 14, 0, 231, 230, 1, 0, 0, 0, 232, 235, 1, 0, 0, 0, 233, 231, 1, 0, 0, 0, 233, 234, 1, 0, 0, 0, 234, 237, 1, 0, 0, 0, 235, 233, 1, 0, 0, 0, 236, 238, 3, 26, 13, 0, 237, 236, 1, 0, 0, 0, 237, 238, 1, 0, 0, 0, 238, 23, 1, 0, 0, 0, 239, 240, 5, 13, 0, 0, 240, 241, 5, 3, 0, 0, 241, 242, 3, 30, 15, 0, 242, 243, 5, 4, 0, 0, 243, 244, 3, 32, 16, 0, 244, 25, 1, 0, 0, 0, 245, 246, 5, 14, 0, 0, 246, 247, 3, 32, 16, 0, 247, 27, 1, 0, 0, 0, 248, 249, 5, 14, 0, 0, 249, 250, 5, 13, 0, 0, 250, 251, 5, 3, 0, 0, 251, 252, 3, 30, 15, 0, 252, 253, 5, 4, 0, 0, 253, 254, 3, 32, 16, 0, 254, 29, 1, 0, 0, 0, 255, 256, 3, 102, 51, 0, 256, 257, 3, 126, 63, 0, 257, 258, 3, 42, 21, 0, 258, 31, 1, 0, 0, 0, 259, 261, 5, 11, 0, 0, 260, 262, 3, 44, 22, 0, 261, 260, 1, 0, 0, 0, 262, 263, 1, 0, 0, 0, 263, 261, 1, 0, 0, 0, 263, 264, 1, 0, 0, 0, 264, 265, 1, 0, 0, 0, 265, 266, 5, 12, 0, 0, 266, 269, 1, 0, 0, 0, 267, 269, 3, 44, 22, 0, 268, 259, 1, 0, 0, 0, 268, 267, 1, 0, 0, 0, 269, 33, 1, 0, 0, 0, 270, 272, 3, 36, 18, 0, 271, 273, 3, 38, 19, 0, 272, 271, 1, 0, 0, 0, 273, 274, 1, 0, 0, 0, 274, 272, 1, 0, 0, 0, 274, 275, 1, 0, 0, 0, 275, 277, 1, 0, 0, 0, 276, 278, 3, 40, 20, 0, 277, 276, 1, 0, 0, 0, 277, 278, 1, 0, 0, 0, 278, 279, 1, 0, 0, 0, 279, 280, 5, 15, 0, 0, 280, 35, 1, 0, 0, 0, 281, 282, 5, 16, 0, 0, 282, 283, 5, 3, 0, 0, 283, 284, 3, 102, 51, 0, 284, 285, 5, 4, 0, 0, 285, 37, 1, 0, 0, 0, 286, 287, 3, 42, 21, 0, 287, 288, 5, 17, 0, 0, 288, 289, 3, 32, 16, 0, 289, 39, 1, 0, 0, 0, 290, 291, 5, 18, 0, 0, 291, 292, 5, 17, 0, 0, 292, 293, 3, 32, 16, 0, 293, 41, 1, 0, 0, 0, 294, 295, 3, 128, 64, 0, 295, 43, 1, 0, 0, 0, 296, 297, 3, 102, 51, 0, 297, 301, 5, 19, 0, 0, 298, 302, 3, 128, 64, 0, 299, 302, 3, 98, 49, 0, 300, 302, 3, 86, 43, 0, 301, 298, 1, 0, 0, 0, 301, 299, 1, 0, 0, 0, 301, 300, 1, 0, 0, 0, 302, 303, 1, 0, 0, 0, 303, 304, 5, 5, 0, 0, 304, 45, 1, 0, 0, 0, 305, 310, 3, 48, 24, 0, 306, 307, 5, 6, 0, 0, 307, 309, 3, 48, 24, 0, 308, 306, 1, 0, 0, 0, 309, 312, 1, 0, 0, 0, 310, 308, 1, 0, 0, 0, 310, 311, 1, 0, 0, 0, 311, 47, 1, 0, 0, 0, 312, 310, 1, 0, 0, 0, 313, 315, 5, 66, 0, 0, 314, 316, 3, 96, 48, 0, 315, 314, 1, 0, 0, 0, 315, 316, 1, 0, 0, 0, 316, 49, 1, 0, 0, 0, 317, 318, 3, 52, 26, 0, 318, 319, 5, 5, 0, 0, 319, 322, 1, 0, 0, 0, 320, 322, 3, 106, 53, 0, 321, 317, 1, 0, 0, 0, 321, 320, 1, 0, 0, 0, 322, 51, 1, 0, 0, 0, 323, 329, 3, 104, 52, 0, 324, 329, 3, 80, 40, 0, 325, 329, 3, 68, 34, 0, 326, 329, 3, 82, 41, 0, 327, 329, 3, 54, 27, 0, 328, 323, 1, 0, 0, 0, 328, 324, 1, 0, 0, 0, 328, 325, 1, 0, 0, 0, 328, 326, 1, 0, 0, 0, 328, 327, 1, 0, 0, 0, 329, 53, 1, 0, 0, 0, 330, 331, 3, 66, 33, 0, 331, 332, 3, 64, 32, 0, 332, 333, 5, 3, 0, 0, 333, 334, 3, 56, 28, 0, 334, 335, 5, 4, 0, 0, 335, 55, 1, 0, 0, 0, 336, 339, 3, 58, 29, 0, 337, 339, 3, 60, 30, 0, 338, 336, 1, 0, 0, 0, 338, 337, 1, 0, 0, 0, 339, 57, 1, 0, 0, 0, 340, 345, 3, 10, 5, 0, 341, 342, 5, 6, 0, 0, 342, 344, 3, 10, 5, 0, 343, 341, 1, 0, 0, 0, 344, 347, 1, 0, 0, 0, 345, 343, 1, 0, 0, 0, 345, 346, 1, 0, 0, 0, 346, 59, 1, 0, 0, 0, 347, 345, 1, 0, 0, 0, 348, 353, 3, 62, 31, 0, 349, 350, 5, 6, 0, 0, 350, 352, 3, 62, 31, 0, 351, 349, 1, 0, 0, 0, 352, 355, 1, 0, 0, 0, 353, 351, 1, 0, 0, 0, 353, 354, 1, 0, 0, 0, 354, 61, 1, 0, 0, 0, 355, 353, 1, 0, 0, 0, 356, 357, 5, 20, 0, 0, 357, 358, 5, 66, 0, 0, 358, 359, 5, 3, 0, 0, 359, 360, 3, 98, 49, 0, 360, 361, 5, 4, 0, 0, 361, 63, 1, 0, 0, 0, 362, 363, 5, 66, 0, 0, 363, 65, 1, 0, 0, 0, 364, 365, 5, 66, 0, 0, 365, 67, 1, 0, 0, 0, 366, 372, 3, 72, 36, 0, 367, 372, 3, 74, 37, 0, 368, 372, 3, 78, 39, 0, 369, 372, 3, 70, 35, 0, 370, 372, 3, 76, 38, 0, 371, 366, 1, 0, 0, 0, 371, 367, 1, 0, 0, 0, 371, 368, 1, 0, 0, 0, 371, 369, 1, 0, 0, 0, 371, 370, 1, 0, 0, 0, 372, 69, 1, 0, 0, 0, 373, 374, 5, 21, 0, 0, 374, 379, 3, 14, 7, 0, 375, 376, 5, 6, 0, 0, 376, 378, 3, 14, 7, 0, 377, 375, 1, 0, 0, 0, 378, 381, 1, 0, 0, 0, 379, 377, 1, 0, 0, 0, 379, 380, 1, 0, 0, 0, 380, 71, 1, 0, 0, 0, 381, 379, 1, 0, 0, 0, 382, 383, 5, 22, 0, 0, 383, 388, 3, 14, 7, 0, 384, 385, 5, 6, 0, 0, 385, 387, 3, 14, 7, 0, 386, 384, 1, 0, 0, 0, 387, 390, 1, 0, 0, 0, 388, 386, 1, 0, 0, 0, 388, 389, 1, 0, 0, 0, 389, 73, 1, 0, 0, 0, 390, 388, 1, 0, 0, 0, 391, 392, 5, 23, 0, 0, 392, 397, 3, 14, 7, 0, 393, 394, 5, 6, 0, 0, 394, 396, 3, 14, 7, 0, 395, 393, 1, 0, 0, 0, 396, 399, 1, 0, 0, 0, 397, 395, 1, 0, 0, 0, 397, 398, 1, 0, 0, 0, 398, 75, 1, 0, 0, 0, 399, 397, 1, 0, 0, 0, 400, 401, 5, 24, 0, 0, 401, 406, 3, 14, 7, 0, 402, 403, 5, 6, 0, 0, 403, 405, 3, 14, 7, 0, 404, 402, 1, 0, 0, 0, 405, 408, 1, 0, 0, 0, 406, 404, 1, 0, 0, 0, 406, 407, 1, 0, 0, 0, 407, 77, 1, 0, 0, 0, 408, 406, 1, 0, 0, 0, 409, 410, 5, 25, 0, 0, 410, 415, 3, 82, 41, 0, 411, 412, 5, 6, 0, 0, 412, 414, 3, 82, 41, 0, 413, 411, 1, 0, 0, 0, 414, 417, 1, 0, 0, 0, 415, 413, 1, 0, 0, 0, 415, 416, 1, 0, 0, 0, 416, 79, 1, 0, 0, 0, 417, 415, 1, 0, 0, 0, 418, 419, 5, 26, 0, 0, 419, 420, 3, 102, 51, 0, 420, 423, 5, 27, 0, 0, 421, 424, 3, 84, 42, 0, 422, 424, 3, 86, 43, 0, 423, 421, 1, 0, 0, 0, 423, 422, 1, 0, 0, 0, 424, 81, 1, 0, 0, 0, 425, 426, 5, 66, 0, 0, 426, 429, 5, 27, 0, 0, 427, 430, 3, 84, 42, 0, 428, 430, 3, 86, 43, 0, 429, 427, 1, 0, 0, 0, 429, 428, 1, 0, 0, 0, 430, 83, 1, 0, 0, 0, 431, 433, 3, 120, 60, 0, 432, 431, 1, 0, 0, 0, 432, 433, 1, 0, 0, 0, 433, 434, 1, 0, 0, 0, 434, 435, 5, 3, 0, 0, 435, 436, 3, 86, 43, 0, 436, 437, 5, 4, 0, 0, 437, 85, 1, 0, 0, 0, 438, 441, 3, 84, 42, 0, 439, 441, 3, 88, 44, 0, 440, 438, 1, 0, 0, 0, 440, 439, 1, 0, 0, 0, 441, 449, 1, 0, 0, 0, 442, 445, 3, 122, 61, 0, 443, 446, 3, 84, 42, 0, 444, 446, 3, 88, 44, 0, 445, 443, 1, 0, 0, 0, 445, 444, 1, 0, 0, 0, 446, 448, 1, 0, 0, 0, 447, 442, 1, 0, 0, 0, 448, 451, 1, 0, 0, 0, 449, 447, 1, 0, 0, 0, 449, 450, 1, 0, 0, 0, 450, 87, 1, 0, 0, 0, 451, 449, 1, 0, 0, 0, 452, 454, 3, 120, 60, 0, 453, 452, 1, 0, 0, 0, 453, 454, 1, 0, 0, 0, 454, 455, 1, 0, 0, 0, 455, 458, 3, 98, 49, 0, 456, 458, 3, 128, 64, 0, 457, 453, 1, 0, 0, 0, 457, 456, 1, 0, 0, 0, 458, 89, 1, 0, 0, 0, 459, 462, 3, 84, 42, 0, 460, 462, 3, 88, 44, 0, 461, 459, 1, 0, 0, 0, 461, 460, 1, 0, 0, 0, 462, 470, 1, 0, 0, 0, 463, 466, 3, 122, 61, 0, 464, 467, 3, 84, 42, 0, 465, 467, 3, 88, 44, 0, 466, 464, 1, 0, 0, 0, 466, 465, 1, 0, 0, 0, 467, 469, 1, 0, 0, 0, 468, 463, 1, 0, 0, 0, 469, 472, 1, 0, 0, 0, 470, 468, 1, 0, 0, 0, 470, 471, 1, 0, 0, 0, 471, 91, 1, 0, 0, 0, 472, 470, 1, 0, 0, 0, 473, 474, 3, 90, 45, 0, 474, 475, 3, 126, 63, 0, 475, 476, 3, 94, 47, 0, 476, 93, 1, 0, 0, 0, 477, 478, 3, 128, 64, 0, 478, 95, 1, 0, 0, 0, 479, 480, 5, 28, 0, 0, 480, 483, 5, 72, 0, 0, 481, 482, 5, 17, 0, 0, 482, 484, 5, 72, 0, 0, 483, 481, 1, 0, 0, 0, 483, 484, 1, 0, 0, 0, 484, 485, 1, 0, 0, 0, 485, 486, 5, 29, 0, 0, 486, 97, 1, 0, 0, 0, 487, 490, 3, 10, 5, 0, 488, 490, 3, 100, 50, 0, 489, 487, 1, 0, 0, 0, 489, 488, 1, 0, 0, 0, 490, 99, 1, 0, 0, 0, 491, 492, 5, 30, 0, 0, 492, 497, 3, 10, 5, 0, 493, 494, 5, 6, 0, 0, 494, 496, 3, 10, 5, 0, 495, 493, 1, 0, 0, 0, 496, 499, 1, 0, 0, 0, 497, 495, 1, 0, 0, 0, 497, 498, 1, 0, 0, 0, 498, 500, 1, 0, 0, 0, 499, 497, 1, 0, 0, 0, 500, 502, 5, 31, 0, 0, 501, 503, 3, 96, 48, 0, 502, 501, 1, 0, 0, 0, 502, 503, 1, 0, 0, 0, 503, 101, 1, 0, 0, 0, 504, 505, 3, 98, 49, 0, 505, 103, 1, 0, 0, 0, 506, 507, 3, 12, 6, 0, 507, 105, 1, 0, 0, 0, 508, 512, 3, 114, 57, 0, 509, 512, 3, 108, 54, 0, 510, 512, 3, 110, 55, 0, 511, 508, 1, 0, 0, 0, 511, 509, 1, 0, 0, 0, 511, 510, 1, 0, 0, 0, 512, 107, 1, 0, 0, 0, 513, 514, 5, 32, 0, 0, 514, 516, 5, 33, 0, 0, 515, 517, 5, 66, 0, 0, 516, 515, 1, 0, 0, 0, 517, 518, 1, 0, 0, 0, 518, 516, 1, 0, 0, 0, 518, 519, 1, 0, 0, 0, 519, 520, 1, 0, 0, 0, 520, 521, 5, 33, 0, 0, 521, 524, 5, 33, 0, 0, 522, 525, 3, 112, 56, 0, 523, 525, 7, 0, 0, 0, 524, 522, 1, 0, 0, 0, 524, 523, 1, 0, 0, 0, 525, 526, 1, 0, 0, 0, 526, 527, 5, 33, 0, 0, 527, 109, 1, 0, 0, 0, 528, 529, 5, 34, 0, 0, 529, 530, 5, 66, 0, 0, 530, 531, 5, 66, 0, 0, 531, 111, 1, 0, 0, 0, 532, 535, 3, 122, 61, 0, 533, 535, 3, 120, 60, 0, 534, 532, 1, 0, 0, 0, 534, 533, 1, 0, 0, 0, 535, 113, 1, 0, 0, 0, 536, 537, 5, 35, 0, 0, 537, 538, 5, 33, 0, 0, 538, 539, 3, 112, 56, 0, 539, 540, 5, 33, 0, 0, 540, 545, 3, 116, 58, 0, 541, 542, 7, 1, 0, 0, 542, 544, 3, 116, 58, 0, 543, 541, 1, 0, 0, 0, 544, 547, 1, 0, 0, 0, 545, 543, 1, 0, 0, 0, 545, 546, 1, 0, 0, 0, 546, 115, 1, 0, 0, 0, 547, 545, 1, 0, 0, 0, 548, 549, 5, 66, 0, 0, 549, 550, 5, 27, 0, 0, 550, 552, 3, 128, 64, 0, 551, 553, 3, 118, 59, 0, 552, 551, 1, 0, 0, 0, 552, 553, 1, 0, 0, 0, 553, 579, 1, 0, 0, 0, 554, 555, 5, 66, 0, 0, 555, 556, 5, 38, 0, 0, 556, 558, 3, 128, 64, 0, 557, 559, 3, 118, 59, 0, 558, 557, 1, 0, 0, 0, 558, 559, 1, 0, 0, 0, 559, 579, 1, 0, 0, 0, 560, 561, 5, 66, 0, 0, 561, 562, 5, 39, 0, 0, 562, 564, 3, 128, 64, 0, 563, 565, 3, 118, 59, 0, 564, 563, 1, 0, 0, 0, 564, 565, 1, 0, 0, 0, 565, 579, 1, 0, 0, 0, 566, 567, 5, 66, 0, 0, 567, 568, 5, 40, 0, 0, 568, 570, 3, 128, 64, 0, 569, 571, 3, 118, 59, 0, 570, 569, 1, 0, 0, 0, 570, 571, 1, 0, 0, 0, 571, 579, 1, 0, 0, 0, 572, 573, 5, 66, 0, 0, 573, 574, 5, 19, 0, 0, 574, 576, 3, 128, 64, 0, 575, 577, 3, 118, 59, 0, 576, 575, 1, 0, 0, 0, 576, 577, 1, 0, 0, 0, 577, 579, 1, 0, 0, 0, 578, 548, 1, 0, 0, 0, 578, 554, 1, 0, 0, 0, 578, 560, 1, 0, 0, 0, 578, 566, 1, 0, 0, 0, 578, 572, 1, 0, 0, 0, 579, 117, 1, 0, 0, 0, 580, 581, 5, 66, 0, 0, 581, 119, 1, 0, 0, 0, 582, 583, 7, 2, 0, 0, 583, 121, 1, 0, 0, 0, 584, 585, 7, 3, 0, 0, 585, 123, 1, 0, 0, 0, 586, 587, 7, 4, 0, 0, 587, 125, 1, 0, 0, 0, 588, 589, 7, 5, 0, 0, 589, 127, 1, 0, 0, 0, 590, 591, 7, 6, 0, 0, 591, 129, 1, 0, 0, 0, 61, 133, 145, 151, 153, 160, 168, 171, 175, 183, 192, 201, 204, 207, 222, 227, 233, 237, 263, 268, 274, 277, 301, 310, 315, 321, 328, 338, 345, 353, 371, 379, 388, 397, 406, 415, 423, 429, 432, 440, 445, 449, 453, 457, 461, 466, 470, 483, 489, 497, 502, 511, 518, 524, 534, 545, 552, 558, 564, 570, 576, 578] \ No newline at end of file diff --git a/lfr/antlrgen/lfr/lfrXLexer.interp b/lfr/antlrgen/lfr/lfrXLexer.interp index 8635d76..f408e7a 100755 --- a/lfr/antlrgen/lfr/lfrXLexer.interp +++ b/lfr/antlrgen/lfr/lfrXLexer.interp @@ -257,4 +257,4 @@ mode names: DEFAULT_MODE atn: -[3, 24715, 42794, 33075, 47597, 16764, 15335, 30598, 22884, 2, 77, 670, 8, 1, 4, 2, 9, 2, 4, 3, 9, 3, 4, 4, 9, 4, 4, 5, 9, 5, 4, 6, 9, 6, 4, 7, 9, 7, 4, 8, 9, 8, 4, 9, 9, 9, 4, 10, 9, 10, 4, 11, 9, 11, 4, 12, 9, 12, 4, 13, 9, 13, 4, 14, 9, 14, 4, 15, 9, 15, 4, 16, 9, 16, 4, 17, 9, 17, 4, 18, 9, 18, 4, 19, 9, 19, 4, 20, 9, 20, 4, 21, 9, 21, 4, 22, 9, 22, 4, 23, 9, 23, 4, 24, 9, 24, 4, 25, 9, 25, 4, 26, 9, 26, 4, 27, 9, 27, 4, 28, 9, 28, 4, 29, 9, 29, 4, 30, 9, 30, 4, 31, 9, 31, 4, 32, 9, 32, 4, 33, 9, 33, 4, 34, 9, 34, 4, 35, 9, 35, 4, 36, 9, 36, 4, 37, 9, 37, 4, 38, 9, 38, 4, 39, 9, 39, 4, 40, 9, 40, 4, 41, 9, 41, 4, 42, 9, 42, 4, 43, 9, 43, 4, 44, 9, 44, 4, 45, 9, 45, 4, 46, 9, 46, 4, 47, 9, 47, 4, 48, 9, 48, 4, 49, 9, 49, 4, 50, 9, 50, 4, 51, 9, 51, 4, 52, 9, 52, 4, 53, 9, 53, 4, 54, 9, 54, 4, 55, 9, 55, 4, 56, 9, 56, 4, 57, 9, 57, 4, 58, 9, 58, 4, 59, 9, 59, 4, 60, 9, 60, 4, 61, 9, 61, 4, 62, 9, 62, 4, 63, 9, 63, 4, 64, 9, 64, 4, 65, 9, 65, 4, 66, 9, 66, 4, 67, 9, 67, 4, 68, 9, 68, 4, 69, 9, 69, 4, 70, 9, 70, 4, 71, 9, 71, 4, 72, 9, 72, 4, 73, 9, 73, 4, 74, 9, 74, 4, 75, 9, 75, 4, 76, 9, 76, 4, 77, 9, 77, 4, 78, 9, 78, 4, 79, 9, 79, 4, 80, 9, 80, 4, 81, 9, 81, 4, 82, 9, 82, 4, 83, 9, 83, 4, 84, 9, 84, 4, 85, 9, 85, 4, 86, 9, 86, 4, 87, 9, 87, 4, 88, 9, 88, 4, 89, 9, 89, 4, 90, 9, 90, 4, 91, 9, 91, 4, 92, 9, 92, 4, 93, 9, 93, 4, 94, 9, 94, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 3, 4, 3, 5, 3, 5, 3, 6, 3, 6, 3, 7, 3, 7, 3, 8, 3, 8, 3, 8, 3, 8, 3, 8, 3, 8, 3, 8, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 10, 3, 10, 3, 10, 3, 10, 3, 10, 3, 10, 3, 10, 3, 10, 3, 11, 3, 11, 3, 11, 3, 11, 3, 11, 3, 11, 3, 11, 3, 11, 3, 11, 3, 11, 3, 11, 3, 11, 3, 12, 3, 12, 3, 12, 3, 12, 3, 12, 3, 12, 3, 13, 3, 13, 3, 13, 3, 13, 3, 14, 3, 14, 3, 14, 3, 15, 3, 15, 3, 15, 3, 15, 3, 15, 3, 16, 3, 16, 3, 16, 3, 16, 3, 16, 3, 16, 3, 16, 3, 16, 3, 17, 3, 17, 3, 17, 3, 17, 3, 17, 3, 18, 3, 18, 3, 19, 3, 19, 3, 19, 3, 19, 3, 19, 3, 19, 3, 19, 3, 19, 3, 20, 3, 20, 3, 20, 3, 21, 3, 21, 3, 22, 3, 22, 3, 22, 3, 22, 3, 22, 3, 22, 3, 22, 3, 23, 3, 23, 3, 23, 3, 23, 3, 23, 3, 24, 3, 24, 3, 24, 3, 24, 3, 24, 3, 24, 3, 24, 3, 24, 3, 25, 3, 25, 3, 25, 3, 25, 3, 25, 3, 26, 3, 26, 3, 26, 3, 26, 3, 26, 3, 26, 3, 26, 3, 27, 3, 27, 3, 27, 3, 27, 3, 27, 3, 27, 3, 27, 3, 28, 3, 28, 3, 29, 3, 29, 3, 30, 3, 30, 3, 31, 3, 31, 3, 32, 3, 32, 3, 33, 3, 33, 3, 33, 3, 33, 3, 33, 3, 34, 3, 34, 3, 35, 3, 35, 3, 35, 3, 35, 3, 35, 3, 35, 3, 35, 3, 35, 3, 35, 3, 35, 3, 36, 3, 36, 3, 36, 3, 36, 3, 36, 3, 36, 3, 36, 3, 36, 3, 36, 3, 36, 3, 36, 3, 37, 3, 37, 3, 37, 3, 37, 3, 38, 3, 38, 3, 38, 3, 39, 3, 39, 3, 40, 3, 40, 3, 41, 3, 41, 3, 41, 3, 42, 3, 42, 3, 43, 3, 43, 3, 44, 3, 44, 3, 45, 3, 45, 3, 46, 3, 46, 3, 47, 3, 47, 3, 47, 3, 48, 3, 48, 3, 49, 3, 49, 3, 49, 3, 50, 3, 50, 3, 51, 3, 51, 3, 51, 3, 52, 3, 52, 3, 52, 3, 53, 3, 53, 3, 54, 3, 54, 3, 55, 3, 55, 3, 56, 3, 56, 3, 56, 3, 57, 3, 57, 3, 57, 3, 58, 3, 58, 3, 58, 3, 58, 3, 59, 3, 59, 3, 59, 3, 59, 3, 60, 3, 60, 3, 60, 3, 61, 3, 61, 3, 61, 3, 62, 3, 62, 3, 62, 3, 63, 3, 63, 3, 63, 3, 64, 3, 64, 3, 64, 3, 65, 3, 65, 3, 65, 3, 65, 3, 66, 3, 66, 3, 66, 3, 66, 3, 67, 3, 67, 7, 67, 458, 10, 67, 12, 67, 14, 67, 461, 11, 67, 3, 68, 6, 68, 464, 10, 68, 13, 68, 14, 68, 465, 3, 68, 3, 68, 3, 69, 3, 69, 3, 69, 3, 69, 7, 69, 474, 10, 69, 12, 69, 14, 69, 477, 11, 69, 3, 69, 5, 69, 480, 10, 69, 3, 69, 3, 69, 3, 69, 3, 69, 3, 70, 3, 70, 3, 70, 3, 70, 7, 70, 490, 10, 70, 12, 70, 14, 70, 493, 11, 70, 3, 70, 3, 70, 3, 70, 3, 70, 3, 70, 3, 71, 3, 71, 7, 71, 502, 10, 71, 12, 71, 14, 71, 505, 11, 71, 3, 71, 5, 71, 508, 10, 71, 3, 71, 3, 71, 3, 71, 3, 71, 3, 72, 3, 72, 3, 72, 3, 72, 3, 72, 3, 72, 3, 72, 5, 72, 521, 10, 72, 3, 72, 3, 72, 5, 72, 525, 10, 72, 3, 72, 3, 72, 5, 72, 529, 10, 72, 3, 73, 3, 73, 5, 73, 533, 10, 73, 3, 73, 3, 73, 3, 73, 3, 73, 5, 73, 539, 10, 73, 3, 73, 3, 73, 3, 73, 7, 73, 544, 10, 73, 12, 73, 14, 73, 547, 11, 73, 3, 73, 5, 73, 550, 10, 73, 3, 73, 3, 73, 3, 73, 7, 73, 555, 10, 73, 12, 73, 14, 73, 558, 11, 73, 5, 73, 560, 10, 73, 3, 74, 5, 74, 563, 10, 74, 3, 74, 3, 74, 3, 74, 3, 75, 5, 75, 569, 10, 75, 3, 75, 3, 75, 3, 75, 3, 76, 5, 76, 575, 10, 76, 3, 76, 3, 76, 3, 76, 3, 77, 3, 77, 3, 78, 3, 78, 3, 79, 3, 79, 3, 79, 7, 79, 587, 10, 79, 12, 79, 14, 79, 590, 11, 79, 3, 80, 3, 80, 3, 80, 7, 80, 595, 10, 80, 12, 80, 14, 80, 598, 11, 80, 3, 81, 3, 81, 3, 81, 7, 81, 603, 10, 81, 12, 81, 14, 81, 606, 11, 81, 3, 82, 3, 82, 3, 82, 7, 82, 611, 10, 82, 12, 82, 14, 82, 614, 11, 82, 3, 83, 3, 83, 3, 83, 7, 83, 619, 10, 83, 12, 83, 14, 83, 622, 11, 83, 3, 84, 3, 84, 5, 84, 626, 10, 84, 3, 84, 3, 84, 3, 85, 3, 85, 5, 85, 632, 10, 85, 3, 85, 3, 85, 3, 86, 3, 86, 5, 86, 638, 10, 86, 3, 86, 3, 86, 3, 87, 3, 87, 5, 87, 644, 10, 87, 3, 87, 3, 87, 3, 88, 3, 88, 3, 89, 3, 89, 3, 90, 3, 90, 3, 90, 5, 90, 655, 10, 90, 3, 91, 3, 91, 3, 91, 5, 91, 660, 10, 91, 3, 92, 3, 92, 3, 92, 5, 92, 665, 10, 92, 3, 93, 3, 93, 3, 94, 3, 94, 5, 475, 491, 503, 2, 95, 3, 3, 5, 4, 7, 5, 9, 6, 11, 7, 13, 8, 15, 9, 17, 10, 19, 11, 21, 12, 23, 13, 25, 14, 27, 15, 29, 16, 31, 17, 33, 18, 35, 19, 37, 20, 39, 21, 41, 22, 43, 23, 45, 24, 47, 25, 49, 26, 51, 27, 53, 28, 55, 29, 57, 30, 59, 31, 61, 32, 63, 33, 65, 34, 67, 35, 69, 36, 71, 37, 73, 38, 75, 39, 77, 40, 79, 41, 81, 42, 83, 43, 85, 44, 87, 45, 89, 46, 91, 47, 93, 48, 95, 49, 97, 50, 99, 51, 101, 52, 103, 53, 105, 54, 107, 55, 109, 56, 111, 57, 113, 58, 115, 59, 117, 60, 119, 61, 121, 62, 123, 63, 125, 64, 127, 65, 129, 66, 131, 67, 133, 68, 135, 69, 137, 70, 139, 71, 141, 72, 143, 73, 145, 74, 147, 75, 149, 76, 151, 77, 153, 2, 155, 2, 157, 2, 159, 2, 161, 2, 163, 2, 165, 2, 167, 2, 169, 2, 171, 2, 173, 2, 175, 2, 177, 2, 179, 2, 181, 2, 183, 2, 185, 2, 187, 2, 3, 2, 19, 5, 2, 67, 92, 97, 97, 99, 124, 6, 2, 50, 59, 67, 92, 97, 97, 99, 124, 5, 2, 11, 12, 15, 15, 34, 34, 4, 2, 71, 71, 103, 103, 4, 2, 45, 45, 47, 47, 4, 2, 85, 85, 117, 117, 4, 2, 70, 70, 102, 102, 4, 2, 68, 68, 100, 100, 4, 2, 81, 81, 113, 113, 4, 2, 74, 74, 106, 106, 3, 2, 51, 59, 3, 2, 50, 59, 3, 2, 50, 51, 3, 2, 50, 57, 5, 2, 50, 59, 67, 72, 99, 104, 4, 2, 90, 90, 122, 122, 5, 2, 65, 65, 92, 92, 124, 124, 2, 692, 2, 3, 3, 2, 2, 2, 2, 5, 3, 2, 2, 2, 2, 7, 3, 2, 2, 2, 2, 9, 3, 2, 2, 2, 2, 11, 3, 2, 2, 2, 2, 13, 3, 2, 2, 2, 2, 15, 3, 2, 2, 2, 2, 17, 3, 2, 2, 2, 2, 19, 3, 2, 2, 2, 2, 21, 3, 2, 2, 2, 2, 23, 3, 2, 2, 2, 2, 25, 3, 2, 2, 2, 2, 27, 3, 2, 2, 2, 2, 29, 3, 2, 2, 2, 2, 31, 3, 2, 2, 2, 2, 33, 3, 2, 2, 2, 2, 35, 3, 2, 2, 2, 2, 37, 3, 2, 2, 2, 2, 39, 3, 2, 2, 2, 2, 41, 3, 2, 2, 2, 2, 43, 3, 2, 2, 2, 2, 45, 3, 2, 2, 2, 2, 47, 3, 2, 2, 2, 2, 49, 3, 2, 2, 2, 2, 51, 3, 2, 2, 2, 2, 53, 3, 2, 2, 2, 2, 55, 3, 2, 2, 2, 2, 57, 3, 2, 2, 2, 2, 59, 3, 2, 2, 2, 2, 61, 3, 2, 2, 2, 2, 63, 3, 2, 2, 2, 2, 65, 3, 2, 2, 2, 2, 67, 3, 2, 2, 2, 2, 69, 3, 2, 2, 2, 2, 71, 3, 2, 2, 2, 2, 73, 3, 2, 2, 2, 2, 75, 3, 2, 2, 2, 2, 77, 3, 2, 2, 2, 2, 79, 3, 2, 2, 2, 2, 81, 3, 2, 2, 2, 2, 83, 3, 2, 2, 2, 2, 85, 3, 2, 2, 2, 2, 87, 3, 2, 2, 2, 2, 89, 3, 2, 2, 2, 2, 91, 3, 2, 2, 2, 2, 93, 3, 2, 2, 2, 2, 95, 3, 2, 2, 2, 2, 97, 3, 2, 2, 2, 2, 99, 3, 2, 2, 2, 2, 101, 3, 2, 2, 2, 2, 103, 3, 2, 2, 2, 2, 105, 3, 2, 2, 2, 2, 107, 3, 2, 2, 2, 2, 109, 3, 2, 2, 2, 2, 111, 3, 2, 2, 2, 2, 113, 3, 2, 2, 2, 2, 115, 3, 2, 2, 2, 2, 117, 3, 2, 2, 2, 2, 119, 3, 2, 2, 2, 2, 121, 3, 2, 2, 2, 2, 123, 3, 2, 2, 2, 2, 125, 3, 2, 2, 2, 2, 127, 3, 2, 2, 2, 2, 129, 3, 2, 2, 2, 2, 131, 3, 2, 2, 2, 2, 133, 3, 2, 2, 2, 2, 135, 3, 2, 2, 2, 2, 137, 3, 2, 2, 2, 2, 139, 3, 2, 2, 2, 2, 141, 3, 2, 2, 2, 2, 143, 3, 2, 2, 2, 2, 145, 3, 2, 2, 2, 2, 147, 3, 2, 2, 2, 2, 149, 3, 2, 2, 2, 2, 151, 3, 2, 2, 2, 3, 189, 3, 2, 2, 2, 5, 199, 3, 2, 2, 2, 7, 206, 3, 2, 2, 2, 9, 208, 3, 2, 2, 2, 11, 210, 3, 2, 2, 2, 13, 212, 3, 2, 2, 2, 15, 214, 3, 2, 2, 2, 17, 221, 3, 2, 2, 2, 19, 229, 3, 2, 2, 2, 21, 237, 3, 2, 2, 2, 23, 249, 3, 2, 2, 2, 25, 255, 3, 2, 2, 2, 27, 259, 3, 2, 2, 2, 29, 262, 3, 2, 2, 2, 31, 267, 3, 2, 2, 2, 33, 275, 3, 2, 2, 2, 35, 280, 3, 2, 2, 2, 37, 282, 3, 2, 2, 2, 39, 290, 3, 2, 2, 2, 41, 293, 3, 2, 2, 2, 43, 295, 3, 2, 2, 2, 45, 302, 3, 2, 2, 2, 47, 307, 3, 2, 2, 2, 49, 315, 3, 2, 2, 2, 51, 320, 3, 2, 2, 2, 53, 327, 3, 2, 2, 2, 55, 334, 3, 2, 2, 2, 57, 336, 3, 2, 2, 2, 59, 338, 3, 2, 2, 2, 61, 340, 3, 2, 2, 2, 63, 342, 3, 2, 2, 2, 65, 344, 3, 2, 2, 2, 67, 349, 3, 2, 2, 2, 69, 351, 3, 2, 2, 2, 71, 361, 3, 2, 2, 2, 73, 372, 3, 2, 2, 2, 75, 376, 3, 2, 2, 2, 77, 379, 3, 2, 2, 2, 79, 381, 3, 2, 2, 2, 81, 383, 3, 2, 2, 2, 83, 386, 3, 2, 2, 2, 85, 388, 3, 2, 2, 2, 87, 390, 3, 2, 2, 2, 89, 392, 3, 2, 2, 2, 91, 394, 3, 2, 2, 2, 93, 396, 3, 2, 2, 2, 95, 399, 3, 2, 2, 2, 97, 401, 3, 2, 2, 2, 99, 404, 3, 2, 2, 2, 101, 406, 3, 2, 2, 2, 103, 409, 3, 2, 2, 2, 105, 412, 3, 2, 2, 2, 107, 414, 3, 2, 2, 2, 109, 416, 3, 2, 2, 2, 111, 418, 3, 2, 2, 2, 113, 421, 3, 2, 2, 2, 115, 424, 3, 2, 2, 2, 117, 428, 3, 2, 2, 2, 119, 432, 3, 2, 2, 2, 121, 435, 3, 2, 2, 2, 123, 438, 3, 2, 2, 2, 125, 441, 3, 2, 2, 2, 127, 444, 3, 2, 2, 2, 129, 447, 3, 2, 2, 2, 131, 451, 3, 2, 2, 2, 133, 455, 3, 2, 2, 2, 135, 463, 3, 2, 2, 2, 137, 469, 3, 2, 2, 2, 139, 485, 3, 2, 2, 2, 141, 499, 3, 2, 2, 2, 143, 528, 3, 2, 2, 2, 145, 559, 3, 2, 2, 2, 147, 562, 3, 2, 2, 2, 149, 568, 3, 2, 2, 2, 151, 574, 3, 2, 2, 2, 153, 579, 3, 2, 2, 2, 155, 581, 3, 2, 2, 2, 157, 583, 3, 2, 2, 2, 159, 591, 3, 2, 2, 2, 161, 599, 3, 2, 2, 2, 163, 607, 3, 2, 2, 2, 165, 615, 3, 2, 2, 2, 167, 623, 3, 2, 2, 2, 169, 629, 3, 2, 2, 2, 171, 635, 3, 2, 2, 2, 173, 641, 3, 2, 2, 2, 175, 647, 3, 2, 2, 2, 177, 649, 3, 2, 2, 2, 179, 654, 3, 2, 2, 2, 181, 659, 3, 2, 2, 2, 183, 664, 3, 2, 2, 2, 185, 666, 3, 2, 2, 2, 187, 668, 3, 2, 2, 2, 189, 190, 7, 103, 2, 2, 190, 191, 7, 112, 2, 2, 191, 192, 7, 102, 2, 2, 192, 193, 7, 111, 2, 2, 193, 194, 7, 113, 2, 2, 194, 195, 7, 102, 2, 2, 195, 196, 7, 119, 2, 2, 196, 197, 7, 110, 2, 2, 197, 198, 7, 103, 2, 2, 198, 4, 3, 2, 2, 2, 199, 200, 7, 111, 2, 2, 200, 201, 7, 113, 2, 2, 201, 202, 7, 102, 2, 2, 202, 203, 7, 119, 2, 2, 203, 204, 7, 110, 2, 2, 204, 205, 7, 103, 2, 2, 205, 6, 3, 2, 2, 2, 206, 207, 7, 42, 2, 2, 207, 8, 3, 2, 2, 2, 208, 209, 7, 43, 2, 2, 209, 10, 3, 2, 2, 2, 210, 211, 7, 61, 2, 2, 211, 12, 3, 2, 2, 2, 212, 213, 7, 46, 2, 2, 213, 14, 3, 2, 2, 2, 214, 215, 7, 104, 2, 2, 215, 216, 7, 107, 2, 2, 216, 217, 7, 112, 2, 2, 217, 218, 7, 114, 2, 2, 218, 219, 7, 119, 2, 2, 219, 220, 7, 118, 2, 2, 220, 16, 3, 2, 2, 2, 221, 222, 7, 104, 2, 2, 222, 223, 7, 113, 2, 2, 223, 224, 7, 119, 2, 2, 224, 225, 7, 118, 2, 2, 225, 226, 7, 114, 2, 2, 226, 227, 7, 119, 2, 2, 227, 228, 7, 118, 2, 2, 228, 18, 3, 2, 2, 2, 229, 230, 7, 101, 2, 2, 230, 231, 7, 113, 2, 2, 231, 232, 7, 112, 2, 2, 232, 233, 7, 118, 2, 2, 233, 234, 7, 116, 2, 2, 234, 235, 7, 113, 2, 2, 235, 236, 7, 110, 2, 2, 236, 20, 3, 2, 2, 2, 237, 238, 7, 102, 2, 2, 238, 239, 7, 107, 2, 2, 239, 240, 7, 117, 2, 2, 240, 241, 7, 118, 2, 2, 241, 242, 7, 116, 2, 2, 242, 243, 7, 107, 2, 2, 243, 244, 7, 100, 2, 2, 244, 245, 7, 119, 2, 2, 245, 246, 7, 118, 2, 2, 246, 247, 7, 103, 2, 2, 247, 248, 7, 66, 2, 2, 248, 22, 3, 2, 2, 2, 249, 250, 7, 100, 2, 2, 250, 251, 7, 103, 2, 2, 251, 252, 7, 105, 2, 2, 252, 253, 7, 107, 2, 2, 253, 254, 7, 112, 2, 2, 254, 24, 3, 2, 2, 2, 255, 256, 7, 103, 2, 2, 256, 257, 7, 112, 2, 2, 257, 258, 7, 102, 2, 2, 258, 26, 3, 2, 2, 2, 259, 260, 7, 107, 2, 2, 260, 261, 7, 104, 2, 2, 261, 28, 3, 2, 2, 2, 262, 263, 7, 103, 2, 2, 263, 264, 7, 110, 2, 2, 264, 265, 7, 117, 2, 2, 265, 266, 7, 103, 2, 2, 266, 30, 3, 2, 2, 2, 267, 268, 7, 103, 2, 2, 268, 269, 7, 112, 2, 2, 269, 270, 7, 102, 2, 2, 270, 271, 7, 101, 2, 2, 271, 272, 7, 99, 2, 2, 272, 273, 7, 117, 2, 2, 273, 274, 7, 103, 2, 2, 274, 32, 3, 2, 2, 2, 275, 276, 7, 101, 2, 2, 276, 277, 7, 99, 2, 2, 277, 278, 7, 117, 2, 2, 278, 279, 7, 103, 2, 2, 279, 34, 3, 2, 2, 2, 280, 281, 7, 60, 2, 2, 281, 36, 3, 2, 2, 2, 282, 283, 7, 102, 2, 2, 283, 284, 7, 103, 2, 2, 284, 285, 7, 104, 2, 2, 285, 286, 7, 99, 2, 2, 286, 287, 7, 119, 2, 2, 287, 288, 7, 110, 2, 2, 288, 289, 7, 118, 2, 2, 289, 38, 3, 2, 2, 2, 290, 291, 7, 62, 2, 2, 291, 292, 7, 63, 2, 2, 292, 40, 3, 2, 2, 2, 293, 294, 7, 48, 2, 2, 294, 42, 3, 2, 2, 2, 295, 296, 7, 117, 2, 2, 296, 297, 7, 107, 2, 2, 297, 298, 7, 105, 2, 2, 298, 299, 7, 112, 2, 2, 299, 300, 7, 99, 2, 2, 300, 301, 7, 110, 2, 2, 301, 44, 3, 2, 2, 2, 302, 303, 7, 104, 2, 2, 303, 304, 7, 110, 2, 2, 304, 305, 7, 113, 2, 2, 305, 306, 7, 121, 2, 2, 306, 46, 3, 2, 2, 2, 307, 308, 7, 117, 2, 2, 308, 309, 7, 118, 2, 2, 309, 310, 7, 113, 2, 2, 310, 311, 7, 116, 2, 2, 311, 312, 7, 99, 2, 2, 312, 313, 7, 105, 2, 2, 313, 314, 7, 103, 2, 2, 314, 48, 3, 2, 2, 2, 315, 316, 7, 114, 2, 2, 316, 317, 7, 119, 2, 2, 317, 318, 7, 111, 2, 2, 318, 319, 7, 114, 2, 2, 319, 50, 3, 2, 2, 2, 320, 321, 7, 112, 2, 2, 321, 322, 7, 119, 2, 2, 322, 323, 7, 111, 2, 2, 323, 324, 7, 100, 2, 2, 324, 325, 7, 103, 2, 2, 325, 326, 7, 116, 2, 2, 326, 52, 3, 2, 2, 2, 327, 328, 7, 99, 2, 2, 328, 329, 7, 117, 2, 2, 329, 330, 7, 117, 2, 2, 330, 331, 7, 107, 2, 2, 331, 332, 7, 105, 2, 2, 332, 333, 7, 112, 2, 2, 333, 54, 3, 2, 2, 2, 334, 335, 7, 63, 2, 2, 335, 56, 3, 2, 2, 2, 336, 337, 7, 93, 2, 2, 337, 58, 3, 2, 2, 2, 338, 339, 7, 95, 2, 2, 339, 60, 3, 2, 2, 2, 340, 341, 7, 125, 2, 2, 341, 62, 3, 2, 2, 2, 342, 343, 7, 127, 2, 2, 343, 64, 3, 2, 2, 2, 344, 345, 7, 37, 2, 2, 345, 346, 7, 79, 2, 2, 346, 347, 7, 67, 2, 2, 347, 348, 7, 82, 2, 2, 348, 66, 3, 2, 2, 2, 349, 350, 7, 36, 2, 2, 350, 68, 3, 2, 2, 2, 351, 352, 7, 37, 2, 2, 352, 353, 7, 79, 2, 2, 353, 354, 7, 67, 2, 2, 354, 355, 7, 86, 2, 2, 355, 356, 7, 71, 2, 2, 356, 357, 7, 84, 2, 2, 357, 358, 7, 75, 2, 2, 358, 359, 7, 67, 2, 2, 359, 360, 7, 78, 2, 2, 360, 70, 3, 2, 2, 2, 361, 362, 7, 37, 2, 2, 362, 363, 7, 69, 2, 2, 363, 364, 7, 81, 2, 2, 364, 365, 7, 80, 2, 2, 365, 366, 7, 85, 2, 2, 366, 367, 7, 86, 2, 2, 367, 368, 7, 84, 2, 2, 368, 369, 7, 67, 2, 2, 369, 370, 7, 75, 2, 2, 370, 371, 7, 80, 2, 2, 371, 72, 3, 2, 2, 2, 372, 373, 7, 67, 2, 2, 373, 374, 7, 80, 2, 2, 374, 375, 7, 70, 2, 2, 375, 74, 3, 2, 2, 2, 376, 377, 7, 81, 2, 2, 377, 378, 7, 84, 2, 2, 378, 76, 3, 2, 2, 2, 379, 380, 7, 64, 2, 2, 380, 78, 3, 2, 2, 2, 381, 382, 7, 62, 2, 2, 382, 80, 3, 2, 2, 2, 383, 384, 7, 64, 2, 2, 384, 385, 7, 63, 2, 2, 385, 82, 3, 2, 2, 2, 386, 387, 7, 45, 2, 2, 387, 84, 3, 2, 2, 2, 388, 389, 7, 47, 2, 2, 389, 86, 3, 2, 2, 2, 390, 391, 7, 35, 2, 2, 391, 88, 3, 2, 2, 2, 392, 393, 7, 128, 2, 2, 393, 90, 3, 2, 2, 2, 394, 395, 7, 40, 2, 2, 395, 92, 3, 2, 2, 2, 396, 397, 7, 128, 2, 2, 397, 398, 7, 40, 2, 2, 398, 94, 3, 2, 2, 2, 399, 400, 7, 126, 2, 2, 400, 96, 3, 2, 2, 2, 401, 402, 7, 128, 2, 2, 402, 403, 7, 126, 2, 2, 403, 98, 3, 2, 2, 2, 404, 405, 7, 96, 2, 2, 405, 100, 3, 2, 2, 2, 406, 407, 7, 128, 2, 2, 407, 408, 7, 96, 2, 2, 408, 102, 3, 2, 2, 2, 409, 410, 7, 96, 2, 2, 410, 411, 7, 128, 2, 2, 411, 104, 3, 2, 2, 2, 412, 413, 7, 44, 2, 2, 413, 106, 3, 2, 2, 2, 414, 415, 7, 49, 2, 2, 415, 108, 3, 2, 2, 2, 416, 417, 7, 39, 2, 2, 417, 110, 3, 2, 2, 2, 418, 419, 7, 63, 2, 2, 419, 420, 7, 63, 2, 2, 420, 112, 3, 2, 2, 2, 421, 422, 7, 35, 2, 2, 422, 423, 7, 63, 2, 2, 423, 114, 3, 2, 2, 2, 424, 425, 7, 63, 2, 2, 425, 426, 7, 63, 2, 2, 426, 427, 7, 63, 2, 2, 427, 116, 3, 2, 2, 2, 428, 429, 7, 35, 2, 2, 429, 430, 7, 63, 2, 2, 430, 431, 7, 63, 2, 2, 431, 118, 3, 2, 2, 2, 432, 433, 7, 40, 2, 2, 433, 434, 7, 40, 2, 2, 434, 120, 3, 2, 2, 2, 435, 436, 7, 126, 2, 2, 436, 437, 7, 126, 2, 2, 437, 122, 3, 2, 2, 2, 438, 439, 7, 44, 2, 2, 439, 440, 7, 44, 2, 2, 440, 124, 3, 2, 2, 2, 441, 442, 7, 64, 2, 2, 442, 443, 7, 64, 2, 2, 443, 126, 3, 2, 2, 2, 444, 445, 7, 62, 2, 2, 445, 446, 7, 62, 2, 2, 446, 128, 3, 2, 2, 2, 447, 448, 7, 64, 2, 2, 448, 449, 7, 64, 2, 2, 449, 450, 7, 64, 2, 2, 450, 130, 3, 2, 2, 2, 451, 452, 7, 62, 2, 2, 452, 453, 7, 62, 2, 2, 453, 454, 7, 62, 2, 2, 454, 132, 3, 2, 2, 2, 455, 459, 9, 2, 2, 2, 456, 458, 9, 3, 2, 2, 457, 456, 3, 2, 2, 2, 458, 461, 3, 2, 2, 2, 459, 457, 3, 2, 2, 2, 459, 460, 3, 2, 2, 2, 460, 134, 3, 2, 2, 2, 461, 459, 3, 2, 2, 2, 462, 464, 9, 4, 2, 2, 463, 462, 3, 2, 2, 2, 464, 465, 3, 2, 2, 2, 465, 463, 3, 2, 2, 2, 465, 466, 3, 2, 2, 2, 466, 467, 3, 2, 2, 2, 467, 468, 8, 68, 2, 2, 468, 136, 3, 2, 2, 2, 469, 470, 7, 49, 2, 2, 470, 471, 7, 49, 2, 2, 471, 475, 3, 2, 2, 2, 472, 474, 11, 2, 2, 2, 473, 472, 3, 2, 2, 2, 474, 477, 3, 2, 2, 2, 475, 476, 3, 2, 2, 2, 475, 473, 3, 2, 2, 2, 476, 479, 3, 2, 2, 2, 477, 475, 3, 2, 2, 2, 478, 480, 7, 15, 2, 2, 479, 478, 3, 2, 2, 2, 479, 480, 3, 2, 2, 2, 480, 481, 3, 2, 2, 2, 481, 482, 7, 12, 2, 2, 482, 483, 3, 2, 2, 2, 483, 484, 8, 69, 3, 2, 484, 138, 3, 2, 2, 2, 485, 486, 7, 49, 2, 2, 486, 487, 7, 44, 2, 2, 487, 491, 3, 2, 2, 2, 488, 490, 11, 2, 2, 2, 489, 488, 3, 2, 2, 2, 490, 493, 3, 2, 2, 2, 491, 492, 3, 2, 2, 2, 491, 489, 3, 2, 2, 2, 492, 494, 3, 2, 2, 2, 493, 491, 3, 2, 2, 2, 494, 495, 7, 44, 2, 2, 495, 496, 7, 49, 2, 2, 496, 497, 3, 2, 2, 2, 497, 498, 8, 70, 3, 2, 498, 140, 3, 2, 2, 2, 499, 503, 7, 98, 2, 2, 500, 502, 11, 2, 2, 2, 501, 500, 3, 2, 2, 2, 502, 505, 3, 2, 2, 2, 503, 504, 3, 2, 2, 2, 503, 501, 3, 2, 2, 2, 504, 507, 3, 2, 2, 2, 505, 503, 3, 2, 2, 2, 506, 508, 7, 15, 2, 2, 507, 506, 3, 2, 2, 2, 507, 508, 3, 2, 2, 2, 508, 509, 3, 2, 2, 2, 509, 510, 7, 12, 2, 2, 510, 511, 3, 2, 2, 2, 511, 512, 8, 71, 3, 2, 512, 142, 3, 2, 2, 2, 513, 514, 5, 159, 80, 2, 514, 515, 7, 48, 2, 2, 515, 516, 5, 159, 80, 2, 516, 529, 3, 2, 2, 2, 517, 520, 5, 159, 80, 2, 518, 519, 7, 48, 2, 2, 519, 521, 5, 159, 80, 2, 520, 518, 3, 2, 2, 2, 520, 521, 3, 2, 2, 2, 521, 522, 3, 2, 2, 2, 522, 524, 9, 5, 2, 2, 523, 525, 9, 6, 2, 2, 524, 523, 3, 2, 2, 2, 524, 525, 3, 2, 2, 2, 525, 526, 3, 2, 2, 2, 526, 527, 5, 159, 80, 2, 527, 529, 3, 2, 2, 2, 528, 513, 3, 2, 2, 2, 528, 517, 3, 2, 2, 2, 529, 144, 3, 2, 2, 2, 530, 560, 5, 159, 80, 2, 531, 533, 5, 155, 78, 2, 532, 531, 3, 2, 2, 2, 532, 533, 3, 2, 2, 2, 533, 534, 3, 2, 2, 2, 534, 535, 5, 167, 84, 2, 535, 536, 5, 159, 80, 2, 536, 560, 3, 2, 2, 2, 537, 539, 5, 155, 78, 2, 538, 537, 3, 2, 2, 2, 538, 539, 3, 2, 2, 2, 539, 540, 3, 2, 2, 2, 540, 541, 5, 167, 84, 2, 541, 545, 5, 185, 93, 2, 542, 544, 7, 97, 2, 2, 543, 542, 3, 2, 2, 2, 544, 547, 3, 2, 2, 2, 545, 543, 3, 2, 2, 2, 545, 546, 3, 2, 2, 2, 546, 560, 3, 2, 2, 2, 547, 545, 3, 2, 2, 2, 548, 550, 5, 155, 78, 2, 549, 548, 3, 2, 2, 2, 549, 550, 3, 2, 2, 2, 550, 551, 3, 2, 2, 2, 551, 552, 5, 167, 84, 2, 552, 556, 5, 187, 94, 2, 553, 555, 7, 97, 2, 2, 554, 553, 3, 2, 2, 2, 555, 558, 3, 2, 2, 2, 556, 554, 3, 2, 2, 2, 556, 557, 3, 2, 2, 2, 557, 560, 3, 2, 2, 2, 558, 556, 3, 2, 2, 2, 559, 530, 3, 2, 2, 2, 559, 532, 3, 2, 2, 2, 559, 538, 3, 2, 2, 2, 559, 549, 3, 2, 2, 2, 560, 146, 3, 2, 2, 2, 561, 563, 5, 155, 78, 2, 562, 561, 3, 2, 2, 2, 562, 563, 3, 2, 2, 2, 563, 564, 3, 2, 2, 2, 564, 565, 5, 169, 85, 2, 565, 566, 5, 161, 81, 2, 566, 148, 3, 2, 2, 2, 567, 569, 5, 155, 78, 2, 568, 567, 3, 2, 2, 2, 568, 569, 3, 2, 2, 2, 569, 570, 3, 2, 2, 2, 570, 571, 5, 171, 86, 2, 571, 572, 5, 163, 82, 2, 572, 150, 3, 2, 2, 2, 573, 575, 5, 155, 78, 2, 574, 573, 3, 2, 2, 2, 574, 575, 3, 2, 2, 2, 575, 576, 3, 2, 2, 2, 576, 577, 5, 173, 87, 2, 577, 578, 5, 165, 83, 2, 578, 152, 3, 2, 2, 2, 579, 580, 9, 6, 2, 2, 580, 154, 3, 2, 2, 2, 581, 582, 5, 157, 79, 2, 582, 156, 3, 2, 2, 2, 583, 588, 5, 175, 88, 2, 584, 587, 7, 97, 2, 2, 585, 587, 5, 177, 89, 2, 586, 584, 3, 2, 2, 2, 586, 585, 3, 2, 2, 2, 587, 590, 3, 2, 2, 2, 588, 586, 3, 2, 2, 2, 588, 589, 3, 2, 2, 2, 589, 158, 3, 2, 2, 2, 590, 588, 3, 2, 2, 2, 591, 596, 5, 177, 89, 2, 592, 595, 7, 97, 2, 2, 593, 595, 5, 177, 89, 2, 594, 592, 3, 2, 2, 2, 594, 593, 3, 2, 2, 2, 595, 598, 3, 2, 2, 2, 596, 594, 3, 2, 2, 2, 596, 597, 3, 2, 2, 2, 597, 160, 3, 2, 2, 2, 598, 596, 3, 2, 2, 2, 599, 604, 5, 179, 90, 2, 600, 603, 7, 97, 2, 2, 601, 603, 5, 179, 90, 2, 602, 600, 3, 2, 2, 2, 602, 601, 3, 2, 2, 2, 603, 606, 3, 2, 2, 2, 604, 602, 3, 2, 2, 2, 604, 605, 3, 2, 2, 2, 605, 162, 3, 2, 2, 2, 606, 604, 3, 2, 2, 2, 607, 612, 5, 181, 91, 2, 608, 611, 7, 97, 2, 2, 609, 611, 5, 181, 91, 2, 610, 608, 3, 2, 2, 2, 610, 609, 3, 2, 2, 2, 611, 614, 3, 2, 2, 2, 612, 610, 3, 2, 2, 2, 612, 613, 3, 2, 2, 2, 613, 164, 3, 2, 2, 2, 614, 612, 3, 2, 2, 2, 615, 620, 5, 183, 92, 2, 616, 619, 7, 97, 2, 2, 617, 619, 5, 183, 92, 2, 618, 616, 3, 2, 2, 2, 618, 617, 3, 2, 2, 2, 619, 622, 3, 2, 2, 2, 620, 618, 3, 2, 2, 2, 620, 621, 3, 2, 2, 2, 621, 166, 3, 2, 2, 2, 622, 620, 3, 2, 2, 2, 623, 625, 7, 41, 2, 2, 624, 626, 9, 7, 2, 2, 625, 624, 3, 2, 2, 2, 625, 626, 3, 2, 2, 2, 626, 627, 3, 2, 2, 2, 627, 628, 9, 8, 2, 2, 628, 168, 3, 2, 2, 2, 629, 631, 7, 41, 2, 2, 630, 632, 9, 7, 2, 2, 631, 630, 3, 2, 2, 2, 631, 632, 3, 2, 2, 2, 632, 633, 3, 2, 2, 2, 633, 634, 9, 9, 2, 2, 634, 170, 3, 2, 2, 2, 635, 637, 7, 41, 2, 2, 636, 638, 9, 7, 2, 2, 637, 636, 3, 2, 2, 2, 637, 638, 3, 2, 2, 2, 638, 639, 3, 2, 2, 2, 639, 640, 9, 10, 2, 2, 640, 172, 3, 2, 2, 2, 641, 643, 7, 41, 2, 2, 642, 644, 9, 7, 2, 2, 643, 642, 3, 2, 2, 2, 643, 644, 3, 2, 2, 2, 644, 645, 3, 2, 2, 2, 645, 646, 9, 11, 2, 2, 646, 174, 3, 2, 2, 2, 647, 648, 9, 12, 2, 2, 648, 176, 3, 2, 2, 2, 649, 650, 9, 13, 2, 2, 650, 178, 3, 2, 2, 2, 651, 655, 5, 185, 93, 2, 652, 655, 5, 187, 94, 2, 653, 655, 9, 14, 2, 2, 654, 651, 3, 2, 2, 2, 654, 652, 3, 2, 2, 2, 654, 653, 3, 2, 2, 2, 655, 180, 3, 2, 2, 2, 656, 660, 5, 185, 93, 2, 657, 660, 5, 187, 94, 2, 658, 660, 9, 15, 2, 2, 659, 656, 3, 2, 2, 2, 659, 657, 3, 2, 2, 2, 659, 658, 3, 2, 2, 2, 660, 182, 3, 2, 2, 2, 661, 665, 5, 185, 93, 2, 662, 665, 5, 187, 94, 2, 663, 665, 9, 16, 2, 2, 664, 661, 3, 2, 2, 2, 664, 662, 3, 2, 2, 2, 664, 663, 3, 2, 2, 2, 665, 184, 3, 2, 2, 2, 666, 667, 9, 17, 2, 2, 667, 186, 3, 2, 2, 2, 668, 669, 9, 18, 2, 2, 669, 188, 3, 2, 2, 2, 39, 2, 459, 465, 475, 479, 491, 503, 507, 520, 524, 528, 532, 538, 545, 549, 556, 559, 562, 568, 574, 586, 588, 594, 596, 602, 604, 610, 612, 618, 620, 625, 631, 637, 643, 654, 659, 664, 4, 8, 2, 2, 2, 3, 2] \ No newline at end of file +[4, 0, 75, 668, 6, -1, 2, 0, 7, 0, 2, 1, 7, 1, 2, 2, 7, 2, 2, 3, 7, 3, 2, 4, 7, 4, 2, 5, 7, 5, 2, 6, 7, 6, 2, 7, 7, 7, 2, 8, 7, 8, 2, 9, 7, 9, 2, 10, 7, 10, 2, 11, 7, 11, 2, 12, 7, 12, 2, 13, 7, 13, 2, 14, 7, 14, 2, 15, 7, 15, 2, 16, 7, 16, 2, 17, 7, 17, 2, 18, 7, 18, 2, 19, 7, 19, 2, 20, 7, 20, 2, 21, 7, 21, 2, 22, 7, 22, 2, 23, 7, 23, 2, 24, 7, 24, 2, 25, 7, 25, 2, 26, 7, 26, 2, 27, 7, 27, 2, 28, 7, 28, 2, 29, 7, 29, 2, 30, 7, 30, 2, 31, 7, 31, 2, 32, 7, 32, 2, 33, 7, 33, 2, 34, 7, 34, 2, 35, 7, 35, 2, 36, 7, 36, 2, 37, 7, 37, 2, 38, 7, 38, 2, 39, 7, 39, 2, 40, 7, 40, 2, 41, 7, 41, 2, 42, 7, 42, 2, 43, 7, 43, 2, 44, 7, 44, 2, 45, 7, 45, 2, 46, 7, 46, 2, 47, 7, 47, 2, 48, 7, 48, 2, 49, 7, 49, 2, 50, 7, 50, 2, 51, 7, 51, 2, 52, 7, 52, 2, 53, 7, 53, 2, 54, 7, 54, 2, 55, 7, 55, 2, 56, 7, 56, 2, 57, 7, 57, 2, 58, 7, 58, 2, 59, 7, 59, 2, 60, 7, 60, 2, 61, 7, 61, 2, 62, 7, 62, 2, 63, 7, 63, 2, 64, 7, 64, 2, 65, 7, 65, 2, 66, 7, 66, 2, 67, 7, 67, 2, 68, 7, 68, 2, 69, 7, 69, 2, 70, 7, 70, 2, 71, 7, 71, 2, 72, 7, 72, 2, 73, 7, 73, 2, 74, 7, 74, 2, 75, 7, 75, 2, 76, 7, 76, 2, 77, 7, 77, 2, 78, 7, 78, 2, 79, 7, 79, 2, 80, 7, 80, 2, 81, 7, 81, 2, 82, 7, 82, 2, 83, 7, 83, 2, 84, 7, 84, 2, 85, 7, 85, 2, 86, 7, 86, 2, 87, 7, 87, 2, 88, 7, 88, 2, 89, 7, 89, 2, 90, 7, 90, 2, 91, 7, 91, 2, 92, 7, 92, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 2, 1, 3, 1, 3, 1, 4, 1, 4, 1, 5, 1, 5, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 11, 1, 11, 1, 11, 1, 11, 1, 12, 1, 12, 1, 12, 1, 13, 1, 13, 1, 13, 1, 13, 1, 13, 1, 14, 1, 14, 1, 14, 1, 14, 1, 14, 1, 14, 1, 14, 1, 14, 1, 15, 1, 15, 1, 15, 1, 15, 1, 15, 1, 16, 1, 16, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 18, 1, 18, 1, 18, 1, 19, 1, 19, 1, 20, 1, 20, 1, 20, 1, 20, 1, 20, 1, 20, 1, 20, 1, 21, 1, 21, 1, 21, 1, 21, 1, 21, 1, 22, 1, 22, 1, 22, 1, 22, 1, 22, 1, 22, 1, 22, 1, 22, 1, 23, 1, 23, 1, 23, 1, 23, 1, 23, 1, 24, 1, 24, 1, 24, 1, 24, 1, 24, 1, 24, 1, 24, 1, 25, 1, 25, 1, 25, 1, 25, 1, 25, 1, 25, 1, 25, 1, 26, 1, 26, 1, 27, 1, 27, 1, 28, 1, 28, 1, 29, 1, 29, 1, 30, 1, 30, 1, 31, 1, 31, 1, 31, 1, 31, 1, 31, 1, 32, 1, 32, 1, 33, 1, 33, 1, 33, 1, 33, 1, 33, 1, 33, 1, 33, 1, 33, 1, 33, 1, 33, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 35, 1, 35, 1, 35, 1, 35, 1, 36, 1, 36, 1, 36, 1, 37, 1, 37, 1, 38, 1, 38, 1, 39, 1, 39, 1, 39, 1, 40, 1, 40, 1, 41, 1, 41, 1, 42, 1, 42, 1, 43, 1, 43, 1, 44, 1, 44, 1, 45, 1, 45, 1, 45, 1, 46, 1, 46, 1, 47, 1, 47, 1, 47, 1, 48, 1, 48, 1, 49, 1, 49, 1, 49, 1, 50, 1, 50, 1, 50, 1, 51, 1, 51, 1, 52, 1, 52, 1, 53, 1, 53, 1, 54, 1, 54, 1, 54, 1, 55, 1, 55, 1, 55, 1, 56, 1, 56, 1, 56, 1, 56, 1, 57, 1, 57, 1, 57, 1, 57, 1, 58, 1, 58, 1, 58, 1, 59, 1, 59, 1, 59, 1, 60, 1, 60, 1, 60, 1, 61, 1, 61, 1, 61, 1, 62, 1, 62, 1, 62, 1, 63, 1, 63, 1, 63, 1, 63, 1, 64, 1, 64, 1, 64, 1, 64, 1, 65, 1, 65, 5, 65, 456, 8, 65, 10, 65, 12, 65, 459, 9, 65, 1, 66, 4, 66, 462, 8, 66, 11, 66, 12, 66, 463, 1, 66, 1, 66, 1, 67, 1, 67, 1, 67, 1, 67, 5, 67, 472, 8, 67, 10, 67, 12, 67, 475, 9, 67, 1, 67, 3, 67, 478, 8, 67, 1, 67, 1, 67, 1, 67, 1, 67, 1, 68, 1, 68, 1, 68, 1, 68, 5, 68, 488, 8, 68, 10, 68, 12, 68, 491, 9, 68, 1, 68, 1, 68, 1, 68, 1, 68, 1, 68, 1, 69, 1, 69, 5, 69, 500, 8, 69, 10, 69, 12, 69, 503, 9, 69, 1, 69, 3, 69, 506, 8, 69, 1, 69, 1, 69, 1, 69, 1, 69, 1, 70, 1, 70, 1, 70, 1, 70, 1, 70, 1, 70, 1, 70, 3, 70, 519, 8, 70, 1, 70, 1, 70, 3, 70, 523, 8, 70, 1, 70, 1, 70, 3, 70, 527, 8, 70, 1, 71, 1, 71, 3, 71, 531, 8, 71, 1, 71, 1, 71, 1, 71, 1, 71, 3, 71, 537, 8, 71, 1, 71, 1, 71, 1, 71, 5, 71, 542, 8, 71, 10, 71, 12, 71, 545, 9, 71, 1, 71, 3, 71, 548, 8, 71, 1, 71, 1, 71, 1, 71, 5, 71, 553, 8, 71, 10, 71, 12, 71, 556, 9, 71, 3, 71, 558, 8, 71, 1, 72, 3, 72, 561, 8, 72, 1, 72, 1, 72, 1, 72, 1, 73, 3, 73, 567, 8, 73, 1, 73, 1, 73, 1, 73, 1, 74, 3, 74, 573, 8, 74, 1, 74, 1, 74, 1, 74, 1, 75, 1, 75, 1, 76, 1, 76, 1, 77, 1, 77, 1, 77, 5, 77, 585, 8, 77, 10, 77, 12, 77, 588, 9, 77, 1, 78, 1, 78, 1, 78, 5, 78, 593, 8, 78, 10, 78, 12, 78, 596, 9, 78, 1, 79, 1, 79, 1, 79, 5, 79, 601, 8, 79, 10, 79, 12, 79, 604, 9, 79, 1, 80, 1, 80, 1, 80, 5, 80, 609, 8, 80, 10, 80, 12, 80, 612, 9, 80, 1, 81, 1, 81, 1, 81, 5, 81, 617, 8, 81, 10, 81, 12, 81, 620, 9, 81, 1, 82, 1, 82, 3, 82, 624, 8, 82, 1, 82, 1, 82, 1, 83, 1, 83, 3, 83, 630, 8, 83, 1, 83, 1, 83, 1, 84, 1, 84, 3, 84, 636, 8, 84, 1, 84, 1, 84, 1, 85, 1, 85, 3, 85, 642, 8, 85, 1, 85, 1, 85, 1, 86, 1, 86, 1, 87, 1, 87, 1, 88, 1, 88, 1, 88, 3, 88, 653, 8, 88, 1, 89, 1, 89, 1, 89, 3, 89, 658, 8, 89, 1, 90, 1, 90, 1, 90, 3, 90, 663, 8, 90, 1, 91, 1, 91, 1, 92, 1, 92, 3, 473, 489, 501, 0, 93, 1, 1, 3, 2, 5, 3, 7, 4, 9, 5, 11, 6, 13, 7, 15, 8, 17, 9, 19, 10, 21, 11, 23, 12, 25, 13, 27, 14, 29, 15, 31, 16, 33, 17, 35, 18, 37, 19, 39, 20, 41, 21, 43, 22, 45, 23, 47, 24, 49, 25, 51, 26, 53, 27, 55, 28, 57, 29, 59, 30, 61, 31, 63, 32, 65, 33, 67, 34, 69, 35, 71, 36, 73, 37, 75, 38, 77, 39, 79, 40, 81, 41, 83, 42, 85, 43, 87, 44, 89, 45, 91, 46, 93, 47, 95, 48, 97, 49, 99, 50, 101, 51, 103, 52, 105, 53, 107, 54, 109, 55, 111, 56, 113, 57, 115, 58, 117, 59, 119, 60, 121, 61, 123, 62, 125, 63, 127, 64, 129, 65, 131, 66, 133, 67, 135, 68, 137, 69, 139, 70, 141, 71, 143, 72, 145, 73, 147, 74, 149, 75, 151, 0, 153, 0, 155, 0, 157, 0, 159, 0, 161, 0, 163, 0, 165, 0, 167, 0, 169, 0, 171, 0, 173, 0, 175, 0, 177, 0, 179, 0, 181, 0, 183, 0, 185, 0, 1, 0, 17, 3, 0, 65, 90, 95, 95, 97, 122, 4, 0, 48, 57, 65, 90, 95, 95, 97, 122, 3, 0, 9, 10, 13, 13, 32, 32, 2, 0, 69, 69, 101, 101, 2, 0, 43, 43, 45, 45, 2, 0, 83, 83, 115, 115, 2, 0, 68, 68, 100, 100, 2, 0, 66, 66, 98, 98, 2, 0, 79, 79, 111, 111, 2, 0, 72, 72, 104, 104, 1, 0, 49, 57, 1, 0, 48, 57, 1, 0, 48, 49, 1, 0, 48, 55, 3, 0, 48, 57, 65, 70, 97, 102, 2, 0, 88, 88, 120, 120, 3, 0, 63, 63, 90, 90, 122, 122, 690, 0, 1, 1, 0, 0, 0, 0, 3, 1, 0, 0, 0, 0, 5, 1, 0, 0, 0, 0, 7, 1, 0, 0, 0, 0, 9, 1, 0, 0, 0, 0, 11, 1, 0, 0, 0, 0, 13, 1, 0, 0, 0, 0, 15, 1, 0, 0, 0, 0, 17, 1, 0, 0, 0, 0, 19, 1, 0, 0, 0, 0, 21, 1, 0, 0, 0, 0, 23, 1, 0, 0, 0, 0, 25, 1, 0, 0, 0, 0, 27, 1, 0, 0, 0, 0, 29, 1, 0, 0, 0, 0, 31, 1, 0, 0, 0, 0, 33, 1, 0, 0, 0, 0, 35, 1, 0, 0, 0, 0, 37, 1, 0, 0, 0, 0, 39, 1, 0, 0, 0, 0, 41, 1, 0, 0, 0, 0, 43, 1, 0, 0, 0, 0, 45, 1, 0, 0, 0, 0, 47, 1, 0, 0, 0, 0, 49, 1, 0, 0, 0, 0, 51, 1, 0, 0, 0, 0, 53, 1, 0, 0, 0, 0, 55, 1, 0, 0, 0, 0, 57, 1, 0, 0, 0, 0, 59, 1, 0, 0, 0, 0, 61, 1, 0, 0, 0, 0, 63, 1, 0, 0, 0, 0, 65, 1, 0, 0, 0, 0, 67, 1, 0, 0, 0, 0, 69, 1, 0, 0, 0, 0, 71, 1, 0, 0, 0, 0, 73, 1, 0, 0, 0, 0, 75, 1, 0, 0, 0, 0, 77, 1, 0, 0, 0, 0, 79, 1, 0, 0, 0, 0, 81, 1, 0, 0, 0, 0, 83, 1, 0, 0, 0, 0, 85, 1, 0, 0, 0, 0, 87, 1, 0, 0, 0, 0, 89, 1, 0, 0, 0, 0, 91, 1, 0, 0, 0, 0, 93, 1, 0, 0, 0, 0, 95, 1, 0, 0, 0, 0, 97, 1, 0, 0, 0, 0, 99, 1, 0, 0, 0, 0, 101, 1, 0, 0, 0, 0, 103, 1, 0, 0, 0, 0, 105, 1, 0, 0, 0, 0, 107, 1, 0, 0, 0, 0, 109, 1, 0, 0, 0, 0, 111, 1, 0, 0, 0, 0, 113, 1, 0, 0, 0, 0, 115, 1, 0, 0, 0, 0, 117, 1, 0, 0, 0, 0, 119, 1, 0, 0, 0, 0, 121, 1, 0, 0, 0, 0, 123, 1, 0, 0, 0, 0, 125, 1, 0, 0, 0, 0, 127, 1, 0, 0, 0, 0, 129, 1, 0, 0, 0, 0, 131, 1, 0, 0, 0, 0, 133, 1, 0, 0, 0, 0, 135, 1, 0, 0, 0, 0, 137, 1, 0, 0, 0, 0, 139, 1, 0, 0, 0, 0, 141, 1, 0, 0, 0, 0, 143, 1, 0, 0, 0, 0, 145, 1, 0, 0, 0, 0, 147, 1, 0, 0, 0, 0, 149, 1, 0, 0, 0, 1, 187, 1, 0, 0, 0, 3, 197, 1, 0, 0, 0, 5, 204, 1, 0, 0, 0, 7, 206, 1, 0, 0, 0, 9, 208, 1, 0, 0, 0, 11, 210, 1, 0, 0, 0, 13, 212, 1, 0, 0, 0, 15, 219, 1, 0, 0, 0, 17, 227, 1, 0, 0, 0, 19, 235, 1, 0, 0, 0, 21, 247, 1, 0, 0, 0, 23, 253, 1, 0, 0, 0, 25, 257, 1, 0, 0, 0, 27, 260, 1, 0, 0, 0, 29, 265, 1, 0, 0, 0, 31, 273, 1, 0, 0, 0, 33, 278, 1, 0, 0, 0, 35, 280, 1, 0, 0, 0, 37, 288, 1, 0, 0, 0, 39, 291, 1, 0, 0, 0, 41, 293, 1, 0, 0, 0, 43, 300, 1, 0, 0, 0, 45, 305, 1, 0, 0, 0, 47, 313, 1, 0, 0, 0, 49, 318, 1, 0, 0, 0, 51, 325, 1, 0, 0, 0, 53, 332, 1, 0, 0, 0, 55, 334, 1, 0, 0, 0, 57, 336, 1, 0, 0, 0, 59, 338, 1, 0, 0, 0, 61, 340, 1, 0, 0, 0, 63, 342, 1, 0, 0, 0, 65, 347, 1, 0, 0, 0, 67, 349, 1, 0, 0, 0, 69, 359, 1, 0, 0, 0, 71, 370, 1, 0, 0, 0, 73, 374, 1, 0, 0, 0, 75, 377, 1, 0, 0, 0, 77, 379, 1, 0, 0, 0, 79, 381, 1, 0, 0, 0, 81, 384, 1, 0, 0, 0, 83, 386, 1, 0, 0, 0, 85, 388, 1, 0, 0, 0, 87, 390, 1, 0, 0, 0, 89, 392, 1, 0, 0, 0, 91, 394, 1, 0, 0, 0, 93, 397, 1, 0, 0, 0, 95, 399, 1, 0, 0, 0, 97, 402, 1, 0, 0, 0, 99, 404, 1, 0, 0, 0, 101, 407, 1, 0, 0, 0, 103, 410, 1, 0, 0, 0, 105, 412, 1, 0, 0, 0, 107, 414, 1, 0, 0, 0, 109, 416, 1, 0, 0, 0, 111, 419, 1, 0, 0, 0, 113, 422, 1, 0, 0, 0, 115, 426, 1, 0, 0, 0, 117, 430, 1, 0, 0, 0, 119, 433, 1, 0, 0, 0, 121, 436, 1, 0, 0, 0, 123, 439, 1, 0, 0, 0, 125, 442, 1, 0, 0, 0, 127, 445, 1, 0, 0, 0, 129, 449, 1, 0, 0, 0, 131, 453, 1, 0, 0, 0, 133, 461, 1, 0, 0, 0, 135, 467, 1, 0, 0, 0, 137, 483, 1, 0, 0, 0, 139, 497, 1, 0, 0, 0, 141, 526, 1, 0, 0, 0, 143, 557, 1, 0, 0, 0, 145, 560, 1, 0, 0, 0, 147, 566, 1, 0, 0, 0, 149, 572, 1, 0, 0, 0, 151, 577, 1, 0, 0, 0, 153, 579, 1, 0, 0, 0, 155, 581, 1, 0, 0, 0, 157, 589, 1, 0, 0, 0, 159, 597, 1, 0, 0, 0, 161, 605, 1, 0, 0, 0, 163, 613, 1, 0, 0, 0, 165, 621, 1, 0, 0, 0, 167, 627, 1, 0, 0, 0, 169, 633, 1, 0, 0, 0, 171, 639, 1, 0, 0, 0, 173, 645, 1, 0, 0, 0, 175, 647, 1, 0, 0, 0, 177, 652, 1, 0, 0, 0, 179, 657, 1, 0, 0, 0, 181, 662, 1, 0, 0, 0, 183, 664, 1, 0, 0, 0, 185, 666, 1, 0, 0, 0, 187, 188, 5, 101, 0, 0, 188, 189, 5, 110, 0, 0, 189, 190, 5, 100, 0, 0, 190, 191, 5, 109, 0, 0, 191, 192, 5, 111, 0, 0, 192, 193, 5, 100, 0, 0, 193, 194, 5, 117, 0, 0, 194, 195, 5, 108, 0, 0, 195, 196, 5, 101, 0, 0, 196, 2, 1, 0, 0, 0, 197, 198, 5, 109, 0, 0, 198, 199, 5, 111, 0, 0, 199, 200, 5, 100, 0, 0, 200, 201, 5, 117, 0, 0, 201, 202, 5, 108, 0, 0, 202, 203, 5, 101, 0, 0, 203, 4, 1, 0, 0, 0, 204, 205, 5, 40, 0, 0, 205, 6, 1, 0, 0, 0, 206, 207, 5, 41, 0, 0, 207, 8, 1, 0, 0, 0, 208, 209, 5, 59, 0, 0, 209, 10, 1, 0, 0, 0, 210, 211, 5, 44, 0, 0, 211, 12, 1, 0, 0, 0, 212, 213, 5, 102, 0, 0, 213, 214, 5, 105, 0, 0, 214, 215, 5, 110, 0, 0, 215, 216, 5, 112, 0, 0, 216, 217, 5, 117, 0, 0, 217, 218, 5, 116, 0, 0, 218, 14, 1, 0, 0, 0, 219, 220, 5, 102, 0, 0, 220, 221, 5, 111, 0, 0, 221, 222, 5, 117, 0, 0, 222, 223, 5, 116, 0, 0, 223, 224, 5, 112, 0, 0, 224, 225, 5, 117, 0, 0, 225, 226, 5, 116, 0, 0, 226, 16, 1, 0, 0, 0, 227, 228, 5, 99, 0, 0, 228, 229, 5, 111, 0, 0, 229, 230, 5, 110, 0, 0, 230, 231, 5, 116, 0, 0, 231, 232, 5, 114, 0, 0, 232, 233, 5, 111, 0, 0, 233, 234, 5, 108, 0, 0, 234, 18, 1, 0, 0, 0, 235, 236, 5, 100, 0, 0, 236, 237, 5, 105, 0, 0, 237, 238, 5, 115, 0, 0, 238, 239, 5, 116, 0, 0, 239, 240, 5, 114, 0, 0, 240, 241, 5, 105, 0, 0, 241, 242, 5, 98, 0, 0, 242, 243, 5, 117, 0, 0, 243, 244, 5, 116, 0, 0, 244, 245, 5, 101, 0, 0, 245, 246, 5, 64, 0, 0, 246, 20, 1, 0, 0, 0, 247, 248, 5, 98, 0, 0, 248, 249, 5, 101, 0, 0, 249, 250, 5, 103, 0, 0, 250, 251, 5, 105, 0, 0, 251, 252, 5, 110, 0, 0, 252, 22, 1, 0, 0, 0, 253, 254, 5, 101, 0, 0, 254, 255, 5, 110, 0, 0, 255, 256, 5, 100, 0, 0, 256, 24, 1, 0, 0, 0, 257, 258, 5, 105, 0, 0, 258, 259, 5, 102, 0, 0, 259, 26, 1, 0, 0, 0, 260, 261, 5, 101, 0, 0, 261, 262, 5, 108, 0, 0, 262, 263, 5, 115, 0, 0, 263, 264, 5, 101, 0, 0, 264, 28, 1, 0, 0, 0, 265, 266, 5, 101, 0, 0, 266, 267, 5, 110, 0, 0, 267, 268, 5, 100, 0, 0, 268, 269, 5, 99, 0, 0, 269, 270, 5, 97, 0, 0, 270, 271, 5, 115, 0, 0, 271, 272, 5, 101, 0, 0, 272, 30, 1, 0, 0, 0, 273, 274, 5, 99, 0, 0, 274, 275, 5, 97, 0, 0, 275, 276, 5, 115, 0, 0, 276, 277, 5, 101, 0, 0, 277, 32, 1, 0, 0, 0, 278, 279, 5, 58, 0, 0, 279, 34, 1, 0, 0, 0, 280, 281, 5, 100, 0, 0, 281, 282, 5, 101, 0, 0, 282, 283, 5, 102, 0, 0, 283, 284, 5, 97, 0, 0, 284, 285, 5, 117, 0, 0, 285, 286, 5, 108, 0, 0, 286, 287, 5, 116, 0, 0, 287, 36, 1, 0, 0, 0, 288, 289, 5, 60, 0, 0, 289, 290, 5, 61, 0, 0, 290, 38, 1, 0, 0, 0, 291, 292, 5, 46, 0, 0, 292, 40, 1, 0, 0, 0, 293, 294, 5, 115, 0, 0, 294, 295, 5, 105, 0, 0, 295, 296, 5, 103, 0, 0, 296, 297, 5, 110, 0, 0, 297, 298, 5, 97, 0, 0, 298, 299, 5, 108, 0, 0, 299, 42, 1, 0, 0, 0, 300, 301, 5, 102, 0, 0, 301, 302, 5, 108, 0, 0, 302, 303, 5, 111, 0, 0, 303, 304, 5, 119, 0, 0, 304, 44, 1, 0, 0, 0, 305, 306, 5, 115, 0, 0, 306, 307, 5, 116, 0, 0, 307, 308, 5, 111, 0, 0, 308, 309, 5, 114, 0, 0, 309, 310, 5, 97, 0, 0, 310, 311, 5, 103, 0, 0, 311, 312, 5, 101, 0, 0, 312, 46, 1, 0, 0, 0, 313, 314, 5, 112, 0, 0, 314, 315, 5, 117, 0, 0, 315, 316, 5, 109, 0, 0, 316, 317, 5, 112, 0, 0, 317, 48, 1, 0, 0, 0, 318, 319, 5, 110, 0, 0, 319, 320, 5, 117, 0, 0, 320, 321, 5, 109, 0, 0, 321, 322, 5, 98, 0, 0, 322, 323, 5, 101, 0, 0, 323, 324, 5, 114, 0, 0, 324, 50, 1, 0, 0, 0, 325, 326, 5, 97, 0, 0, 326, 327, 5, 115, 0, 0, 327, 328, 5, 115, 0, 0, 328, 329, 5, 105, 0, 0, 329, 330, 5, 103, 0, 0, 330, 331, 5, 110, 0, 0, 331, 52, 1, 0, 0, 0, 332, 333, 5, 61, 0, 0, 333, 54, 1, 0, 0, 0, 334, 335, 5, 91, 0, 0, 335, 56, 1, 0, 0, 0, 336, 337, 5, 93, 0, 0, 337, 58, 1, 0, 0, 0, 338, 339, 5, 123, 0, 0, 339, 60, 1, 0, 0, 0, 340, 341, 5, 125, 0, 0, 341, 62, 1, 0, 0, 0, 342, 343, 5, 35, 0, 0, 343, 344, 5, 77, 0, 0, 344, 345, 5, 65, 0, 0, 345, 346, 5, 80, 0, 0, 346, 64, 1, 0, 0, 0, 347, 348, 5, 34, 0, 0, 348, 66, 1, 0, 0, 0, 349, 350, 5, 35, 0, 0, 350, 351, 5, 77, 0, 0, 351, 352, 5, 65, 0, 0, 352, 353, 5, 84, 0, 0, 353, 354, 5, 69, 0, 0, 354, 355, 5, 82, 0, 0, 355, 356, 5, 73, 0, 0, 356, 357, 5, 65, 0, 0, 357, 358, 5, 76, 0, 0, 358, 68, 1, 0, 0, 0, 359, 360, 5, 35, 0, 0, 360, 361, 5, 67, 0, 0, 361, 362, 5, 79, 0, 0, 362, 363, 5, 78, 0, 0, 363, 364, 5, 83, 0, 0, 364, 365, 5, 84, 0, 0, 365, 366, 5, 82, 0, 0, 366, 367, 5, 65, 0, 0, 367, 368, 5, 73, 0, 0, 368, 369, 5, 78, 0, 0, 369, 70, 1, 0, 0, 0, 370, 371, 5, 65, 0, 0, 371, 372, 5, 78, 0, 0, 372, 373, 5, 68, 0, 0, 373, 72, 1, 0, 0, 0, 374, 375, 5, 79, 0, 0, 375, 376, 5, 82, 0, 0, 376, 74, 1, 0, 0, 0, 377, 378, 5, 62, 0, 0, 378, 76, 1, 0, 0, 0, 379, 380, 5, 60, 0, 0, 380, 78, 1, 0, 0, 0, 381, 382, 5, 62, 0, 0, 382, 383, 5, 61, 0, 0, 383, 80, 1, 0, 0, 0, 384, 385, 5, 43, 0, 0, 385, 82, 1, 0, 0, 0, 386, 387, 5, 45, 0, 0, 387, 84, 1, 0, 0, 0, 388, 389, 5, 33, 0, 0, 389, 86, 1, 0, 0, 0, 390, 391, 5, 126, 0, 0, 391, 88, 1, 0, 0, 0, 392, 393, 5, 38, 0, 0, 393, 90, 1, 0, 0, 0, 394, 395, 5, 126, 0, 0, 395, 396, 5, 38, 0, 0, 396, 92, 1, 0, 0, 0, 397, 398, 5, 124, 0, 0, 398, 94, 1, 0, 0, 0, 399, 400, 5, 126, 0, 0, 400, 401, 5, 124, 0, 0, 401, 96, 1, 0, 0, 0, 402, 403, 5, 94, 0, 0, 403, 98, 1, 0, 0, 0, 404, 405, 5, 126, 0, 0, 405, 406, 5, 94, 0, 0, 406, 100, 1, 0, 0, 0, 407, 408, 5, 94, 0, 0, 408, 409, 5, 126, 0, 0, 409, 102, 1, 0, 0, 0, 410, 411, 5, 42, 0, 0, 411, 104, 1, 0, 0, 0, 412, 413, 5, 47, 0, 0, 413, 106, 1, 0, 0, 0, 414, 415, 5, 37, 0, 0, 415, 108, 1, 0, 0, 0, 416, 417, 5, 61, 0, 0, 417, 418, 5, 61, 0, 0, 418, 110, 1, 0, 0, 0, 419, 420, 5, 33, 0, 0, 420, 421, 5, 61, 0, 0, 421, 112, 1, 0, 0, 0, 422, 423, 5, 61, 0, 0, 423, 424, 5, 61, 0, 0, 424, 425, 5, 61, 0, 0, 425, 114, 1, 0, 0, 0, 426, 427, 5, 33, 0, 0, 427, 428, 5, 61, 0, 0, 428, 429, 5, 61, 0, 0, 429, 116, 1, 0, 0, 0, 430, 431, 5, 38, 0, 0, 431, 432, 5, 38, 0, 0, 432, 118, 1, 0, 0, 0, 433, 434, 5, 124, 0, 0, 434, 435, 5, 124, 0, 0, 435, 120, 1, 0, 0, 0, 436, 437, 5, 42, 0, 0, 437, 438, 5, 42, 0, 0, 438, 122, 1, 0, 0, 0, 439, 440, 5, 62, 0, 0, 440, 441, 5, 62, 0, 0, 441, 124, 1, 0, 0, 0, 442, 443, 5, 60, 0, 0, 443, 444, 5, 60, 0, 0, 444, 126, 1, 0, 0, 0, 445, 446, 5, 62, 0, 0, 446, 447, 5, 62, 0, 0, 447, 448, 5, 62, 0, 0, 448, 128, 1, 0, 0, 0, 449, 450, 5, 60, 0, 0, 450, 451, 5, 60, 0, 0, 451, 452, 5, 60, 0, 0, 452, 130, 1, 0, 0, 0, 453, 457, 7, 0, 0, 0, 454, 456, 7, 1, 0, 0, 455, 454, 1, 0, 0, 0, 456, 459, 1, 0, 0, 0, 457, 455, 1, 0, 0, 0, 457, 458, 1, 0, 0, 0, 458, 132, 1, 0, 0, 0, 459, 457, 1, 0, 0, 0, 460, 462, 7, 2, 0, 0, 461, 460, 1, 0, 0, 0, 462, 463, 1, 0, 0, 0, 463, 461, 1, 0, 0, 0, 463, 464, 1, 0, 0, 0, 464, 465, 1, 0, 0, 0, 465, 466, 6, 66, 0, 0, 466, 134, 1, 0, 0, 0, 467, 468, 5, 47, 0, 0, 468, 469, 5, 47, 0, 0, 469, 473, 1, 0, 0, 0, 470, 472, 9, 0, 0, 0, 471, 470, 1, 0, 0, 0, 472, 475, 1, 0, 0, 0, 473, 474, 1, 0, 0, 0, 473, 471, 1, 0, 0, 0, 474, 477, 1, 0, 0, 0, 475, 473, 1, 0, 0, 0, 476, 478, 5, 13, 0, 0, 477, 476, 1, 0, 0, 0, 477, 478, 1, 0, 0, 0, 478, 479, 1, 0, 0, 0, 479, 480, 5, 10, 0, 0, 480, 481, 1, 0, 0, 0, 481, 482, 6, 67, 1, 0, 482, 136, 1, 0, 0, 0, 483, 484, 5, 47, 0, 0, 484, 485, 5, 42, 0, 0, 485, 489, 1, 0, 0, 0, 486, 488, 9, 0, 0, 0, 487, 486, 1, 0, 0, 0, 488, 491, 1, 0, 0, 0, 489, 490, 1, 0, 0, 0, 489, 487, 1, 0, 0, 0, 490, 492, 1, 0, 0, 0, 491, 489, 1, 0, 0, 0, 492, 493, 5, 42, 0, 0, 493, 494, 5, 47, 0, 0, 494, 495, 1, 0, 0, 0, 495, 496, 6, 68, 1, 0, 496, 138, 1, 0, 0, 0, 497, 501, 5, 96, 0, 0, 498, 500, 9, 0, 0, 0, 499, 498, 1, 0, 0, 0, 500, 503, 1, 0, 0, 0, 501, 502, 1, 0, 0, 0, 501, 499, 1, 0, 0, 0, 502, 505, 1, 0, 0, 0, 503, 501, 1, 0, 0, 0, 504, 506, 5, 13, 0, 0, 505, 504, 1, 0, 0, 0, 505, 506, 1, 0, 0, 0, 506, 507, 1, 0, 0, 0, 507, 508, 5, 10, 0, 0, 508, 509, 1, 0, 0, 0, 509, 510, 6, 69, 1, 0, 510, 140, 1, 0, 0, 0, 511, 512, 3, 157, 78, 0, 512, 513, 5, 46, 0, 0, 513, 514, 3, 157, 78, 0, 514, 527, 1, 0, 0, 0, 515, 518, 3, 157, 78, 0, 516, 517, 5, 46, 0, 0, 517, 519, 3, 157, 78, 0, 518, 516, 1, 0, 0, 0, 518, 519, 1, 0, 0, 0, 519, 520, 1, 0, 0, 0, 520, 522, 7, 3, 0, 0, 521, 523, 7, 4, 0, 0, 522, 521, 1, 0, 0, 0, 522, 523, 1, 0, 0, 0, 523, 524, 1, 0, 0, 0, 524, 525, 3, 157, 78, 0, 525, 527, 1, 0, 0, 0, 526, 511, 1, 0, 0, 0, 526, 515, 1, 0, 0, 0, 527, 142, 1, 0, 0, 0, 528, 558, 3, 157, 78, 0, 529, 531, 3, 153, 76, 0, 530, 529, 1, 0, 0, 0, 530, 531, 1, 0, 0, 0, 531, 532, 1, 0, 0, 0, 532, 533, 3, 165, 82, 0, 533, 534, 3, 157, 78, 0, 534, 558, 1, 0, 0, 0, 535, 537, 3, 153, 76, 0, 536, 535, 1, 0, 0, 0, 536, 537, 1, 0, 0, 0, 537, 538, 1, 0, 0, 0, 538, 539, 3, 165, 82, 0, 539, 543, 3, 183, 91, 0, 540, 542, 5, 95, 0, 0, 541, 540, 1, 0, 0, 0, 542, 545, 1, 0, 0, 0, 543, 541, 1, 0, 0, 0, 543, 544, 1, 0, 0, 0, 544, 558, 1, 0, 0, 0, 545, 543, 1, 0, 0, 0, 546, 548, 3, 153, 76, 0, 547, 546, 1, 0, 0, 0, 547, 548, 1, 0, 0, 0, 548, 549, 1, 0, 0, 0, 549, 550, 3, 165, 82, 0, 550, 554, 3, 185, 92, 0, 551, 553, 5, 95, 0, 0, 552, 551, 1, 0, 0, 0, 553, 556, 1, 0, 0, 0, 554, 552, 1, 0, 0, 0, 554, 555, 1, 0, 0, 0, 555, 558, 1, 0, 0, 0, 556, 554, 1, 0, 0, 0, 557, 528, 1, 0, 0, 0, 557, 530, 1, 0, 0, 0, 557, 536, 1, 0, 0, 0, 557, 547, 1, 0, 0, 0, 558, 144, 1, 0, 0, 0, 559, 561, 3, 153, 76, 0, 560, 559, 1, 0, 0, 0, 560, 561, 1, 0, 0, 0, 561, 562, 1, 0, 0, 0, 562, 563, 3, 167, 83, 0, 563, 564, 3, 159, 79, 0, 564, 146, 1, 0, 0, 0, 565, 567, 3, 153, 76, 0, 566, 565, 1, 0, 0, 0, 566, 567, 1, 0, 0, 0, 567, 568, 1, 0, 0, 0, 568, 569, 3, 169, 84, 0, 569, 570, 3, 161, 80, 0, 570, 148, 1, 0, 0, 0, 571, 573, 3, 153, 76, 0, 572, 571, 1, 0, 0, 0, 572, 573, 1, 0, 0, 0, 573, 574, 1, 0, 0, 0, 574, 575, 3, 171, 85, 0, 575, 576, 3, 163, 81, 0, 576, 150, 1, 0, 0, 0, 577, 578, 7, 4, 0, 0, 578, 152, 1, 0, 0, 0, 579, 580, 3, 155, 77, 0, 580, 154, 1, 0, 0, 0, 581, 586, 3, 173, 86, 0, 582, 585, 5, 95, 0, 0, 583, 585, 3, 175, 87, 0, 584, 582, 1, 0, 0, 0, 584, 583, 1, 0, 0, 0, 585, 588, 1, 0, 0, 0, 586, 584, 1, 0, 0, 0, 586, 587, 1, 0, 0, 0, 587, 156, 1, 0, 0, 0, 588, 586, 1, 0, 0, 0, 589, 594, 3, 175, 87, 0, 590, 593, 5, 95, 0, 0, 591, 593, 3, 175, 87, 0, 592, 590, 1, 0, 0, 0, 592, 591, 1, 0, 0, 0, 593, 596, 1, 0, 0, 0, 594, 592, 1, 0, 0, 0, 594, 595, 1, 0, 0, 0, 595, 158, 1, 0, 0, 0, 596, 594, 1, 0, 0, 0, 597, 602, 3, 177, 88, 0, 598, 601, 5, 95, 0, 0, 599, 601, 3, 177, 88, 0, 600, 598, 1, 0, 0, 0, 600, 599, 1, 0, 0, 0, 601, 604, 1, 0, 0, 0, 602, 600, 1, 0, 0, 0, 602, 603, 1, 0, 0, 0, 603, 160, 1, 0, 0, 0, 604, 602, 1, 0, 0, 0, 605, 610, 3, 179, 89, 0, 606, 609, 5, 95, 0, 0, 607, 609, 3, 179, 89, 0, 608, 606, 1, 0, 0, 0, 608, 607, 1, 0, 0, 0, 609, 612, 1, 0, 0, 0, 610, 608, 1, 0, 0, 0, 610, 611, 1, 0, 0, 0, 611, 162, 1, 0, 0, 0, 612, 610, 1, 0, 0, 0, 613, 618, 3, 181, 90, 0, 614, 617, 5, 95, 0, 0, 615, 617, 3, 181, 90, 0, 616, 614, 1, 0, 0, 0, 616, 615, 1, 0, 0, 0, 617, 620, 1, 0, 0, 0, 618, 616, 1, 0, 0, 0, 618, 619, 1, 0, 0, 0, 619, 164, 1, 0, 0, 0, 620, 618, 1, 0, 0, 0, 621, 623, 5, 39, 0, 0, 622, 624, 7, 5, 0, 0, 623, 622, 1, 0, 0, 0, 623, 624, 1, 0, 0, 0, 624, 625, 1, 0, 0, 0, 625, 626, 7, 6, 0, 0, 626, 166, 1, 0, 0, 0, 627, 629, 5, 39, 0, 0, 628, 630, 7, 5, 0, 0, 629, 628, 1, 0, 0, 0, 629, 630, 1, 0, 0, 0, 630, 631, 1, 0, 0, 0, 631, 632, 7, 7, 0, 0, 632, 168, 1, 0, 0, 0, 633, 635, 5, 39, 0, 0, 634, 636, 7, 5, 0, 0, 635, 634, 1, 0, 0, 0, 635, 636, 1, 0, 0, 0, 636, 637, 1, 0, 0, 0, 637, 638, 7, 8, 0, 0, 638, 170, 1, 0, 0, 0, 639, 641, 5, 39, 0, 0, 640, 642, 7, 5, 0, 0, 641, 640, 1, 0, 0, 0, 641, 642, 1, 0, 0, 0, 642, 643, 1, 0, 0, 0, 643, 644, 7, 9, 0, 0, 644, 172, 1, 0, 0, 0, 645, 646, 7, 10, 0, 0, 646, 174, 1, 0, 0, 0, 647, 648, 7, 11, 0, 0, 648, 176, 1, 0, 0, 0, 649, 653, 3, 183, 91, 0, 650, 653, 3, 185, 92, 0, 651, 653, 7, 12, 0, 0, 652, 649, 1, 0, 0, 0, 652, 650, 1, 0, 0, 0, 652, 651, 1, 0, 0, 0, 653, 178, 1, 0, 0, 0, 654, 658, 3, 183, 91, 0, 655, 658, 3, 185, 92, 0, 656, 658, 7, 13, 0, 0, 657, 654, 1, 0, 0, 0, 657, 655, 1, 0, 0, 0, 657, 656, 1, 0, 0, 0, 658, 180, 1, 0, 0, 0, 659, 663, 3, 183, 91, 0, 660, 663, 3, 185, 92, 0, 661, 663, 7, 14, 0, 0, 662, 659, 1, 0, 0, 0, 662, 660, 1, 0, 0, 0, 662, 661, 1, 0, 0, 0, 663, 182, 1, 0, 0, 0, 664, 665, 7, 15, 0, 0, 665, 184, 1, 0, 0, 0, 666, 667, 7, 16, 0, 0, 667, 186, 1, 0, 0, 0, 37, 0, 457, 463, 473, 477, 489, 501, 505, 518, 522, 526, 530, 536, 543, 547, 554, 557, 560, 566, 572, 584, 586, 592, 594, 600, 602, 608, 610, 616, 618, 623, 629, 635, 641, 652, 657, 662, 2, 6, 0, 0, 0, 1, 0] \ No newline at end of file diff --git a/lfr/antlrgen/lfr/lfrXLexer.py b/lfr/antlrgen/lfr/lfrXLexer.py index e64bdbf..394e348 100755 --- a/lfr/antlrgen/lfr/lfrXLexer.py +++ b/lfr/antlrgen/lfr/lfrXLexer.py @@ -1,320 +1,5894 @@ -# Generated from ./lfrX.g4 by ANTLR 4.9.1 -from antlr4 import * -from io import StringIO -from typing.io import TextIO +# Generated from ./lfrX.g4 by ANTLR 4.10.1 import sys +from io import StringIO + +from antlr4 import * +if sys.version_info[1] > 5: + from typing import TextIO +else: + from typing.io import TextIO def serializedATN(): - with StringIO() as buf: - buf.write("\3\u608b\ua72a\u8133\ub9ed\u417c\u3be7\u7786\u5964\2M") - buf.write("\u029e\b\1\4\2\t\2\4\3\t\3\4\4\t\4\4\5\t\5\4\6\t\6\4\7") - buf.write("\t\7\4\b\t\b\4\t\t\t\4\n\t\n\4\13\t\13\4\f\t\f\4\r\t\r") - buf.write("\4\16\t\16\4\17\t\17\4\20\t\20\4\21\t\21\4\22\t\22\4\23") - buf.write("\t\23\4\24\t\24\4\25\t\25\4\26\t\26\4\27\t\27\4\30\t\30") - buf.write("\4\31\t\31\4\32\t\32\4\33\t\33\4\34\t\34\4\35\t\35\4\36") - buf.write("\t\36\4\37\t\37\4 \t \4!\t!\4\"\t\"\4#\t#\4$\t$\4%\t%") - buf.write("\4&\t&\4\'\t\'\4(\t(\4)\t)\4*\t*\4+\t+\4,\t,\4-\t-\4.") - buf.write("\t.\4/\t/\4\60\t\60\4\61\t\61\4\62\t\62\4\63\t\63\4\64") - buf.write("\t\64\4\65\t\65\4\66\t\66\4\67\t\67\48\t8\49\t9\4:\t:") - buf.write("\4;\t;\4<\t<\4=\t=\4>\t>\4?\t?\4@\t@\4A\tA\4B\tB\4C\t") - buf.write("C\4D\tD\4E\tE\4F\tF\4G\tG\4H\tH\4I\tI\4J\tJ\4K\tK\4L\t") - buf.write("L\4M\tM\4N\tN\4O\tO\4P\tP\4Q\tQ\4R\tR\4S\tS\4T\tT\4U\t") - buf.write("U\4V\tV\4W\tW\4X\tX\4Y\tY\4Z\tZ\4[\t[\4\\\t\\\4]\t]\4") - buf.write("^\t^\3\2\3\2\3\2\3\2\3\2\3\2\3\2\3\2\3\2\3\2\3\3\3\3\3") - buf.write("\3\3\3\3\3\3\3\3\3\3\4\3\4\3\5\3\5\3\6\3\6\3\7\3\7\3\b") - buf.write("\3\b\3\b\3\b\3\b\3\b\3\b\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3") - buf.write("\t\3\n\3\n\3\n\3\n\3\n\3\n\3\n\3\n\3\13\3\13\3\13\3\13") - buf.write("\3\13\3\13\3\13\3\13\3\13\3\13\3\13\3\13\3\f\3\f\3\f\3") - buf.write("\f\3\f\3\f\3\r\3\r\3\r\3\r\3\16\3\16\3\16\3\17\3\17\3") - buf.write("\17\3\17\3\17\3\20\3\20\3\20\3\20\3\20\3\20\3\20\3\20") - buf.write("\3\21\3\21\3\21\3\21\3\21\3\22\3\22\3\23\3\23\3\23\3\23") - buf.write("\3\23\3\23\3\23\3\23\3\24\3\24\3\24\3\25\3\25\3\26\3\26") - buf.write("\3\26\3\26\3\26\3\26\3\26\3\27\3\27\3\27\3\27\3\27\3\30") - buf.write("\3\30\3\30\3\30\3\30\3\30\3\30\3\30\3\31\3\31\3\31\3\31") - buf.write("\3\31\3\32\3\32\3\32\3\32\3\32\3\32\3\32\3\33\3\33\3\33") - buf.write("\3\33\3\33\3\33\3\33\3\34\3\34\3\35\3\35\3\36\3\36\3\37") - buf.write("\3\37\3 \3 \3!\3!\3!\3!\3!\3\"\3\"\3#\3#\3#\3#\3#\3#\3") - buf.write("#\3#\3#\3#\3$\3$\3$\3$\3$\3$\3$\3$\3$\3$\3$\3%\3%\3%\3") - buf.write("%\3&\3&\3&\3\'\3\'\3(\3(\3)\3)\3)\3*\3*\3+\3+\3,\3,\3") - buf.write("-\3-\3.\3.\3/\3/\3/\3\60\3\60\3\61\3\61\3\61\3\62\3\62") - buf.write("\3\63\3\63\3\63\3\64\3\64\3\64\3\65\3\65\3\66\3\66\3\67") - buf.write("\3\67\38\38\38\39\39\39\3:\3:\3:\3:\3;\3;\3;\3;\3<\3<") - buf.write("\3<\3=\3=\3=\3>\3>\3>\3?\3?\3?\3@\3@\3@\3A\3A\3A\3A\3") - buf.write("B\3B\3B\3B\3C\3C\7C\u01ca\nC\fC\16C\u01cd\13C\3D\6D\u01d0") - buf.write("\nD\rD\16D\u01d1\3D\3D\3E\3E\3E\3E\7E\u01da\nE\fE\16E") - buf.write("\u01dd\13E\3E\5E\u01e0\nE\3E\3E\3E\3E\3F\3F\3F\3F\7F\u01ea") - buf.write("\nF\fF\16F\u01ed\13F\3F\3F\3F\3F\3F\3G\3G\7G\u01f6\nG") - buf.write("\fG\16G\u01f9\13G\3G\5G\u01fc\nG\3G\3G\3G\3G\3H\3H\3H") - buf.write("\3H\3H\3H\3H\5H\u0209\nH\3H\3H\5H\u020d\nH\3H\3H\5H\u0211") - buf.write("\nH\3I\3I\5I\u0215\nI\3I\3I\3I\3I\5I\u021b\nI\3I\3I\3") - buf.write("I\7I\u0220\nI\fI\16I\u0223\13I\3I\5I\u0226\nI\3I\3I\3") - buf.write("I\7I\u022b\nI\fI\16I\u022e\13I\5I\u0230\nI\3J\5J\u0233") - buf.write("\nJ\3J\3J\3J\3K\5K\u0239\nK\3K\3K\3K\3L\5L\u023f\nL\3") - buf.write("L\3L\3L\3M\3M\3N\3N\3O\3O\3O\7O\u024b\nO\fO\16O\u024e") - buf.write("\13O\3P\3P\3P\7P\u0253\nP\fP\16P\u0256\13P\3Q\3Q\3Q\7") - buf.write("Q\u025b\nQ\fQ\16Q\u025e\13Q\3R\3R\3R\7R\u0263\nR\fR\16") - buf.write("R\u0266\13R\3S\3S\3S\7S\u026b\nS\fS\16S\u026e\13S\3T\3") - buf.write("T\5T\u0272\nT\3T\3T\3U\3U\5U\u0278\nU\3U\3U\3V\3V\5V\u027e") - buf.write("\nV\3V\3V\3W\3W\5W\u0284\nW\3W\3W\3X\3X\3Y\3Y\3Z\3Z\3") - buf.write("Z\5Z\u028f\nZ\3[\3[\3[\5[\u0294\n[\3\\\3\\\3\\\5\\\u0299") - buf.write("\n\\\3]\3]\3^\3^\5\u01db\u01eb\u01f7\2_\3\3\5\4\7\5\t") - buf.write("\6\13\7\r\b\17\t\21\n\23\13\25\f\27\r\31\16\33\17\35\20") - buf.write("\37\21!\22#\23%\24\'\25)\26+\27-\30/\31\61\32\63\33\65") - buf.write("\34\67\359\36;\37= ?!A\"C#E$G%I&K\'M(O)Q*S+U,W-Y.[/]\60") - buf.write("_\61a\62c\63e\64g\65i\66k\67m8o9q:s;u{?}@\177A\u0081") - buf.write("B\u0083C\u0085D\u0087E\u0089F\u008bG\u008dH\u008fI\u0091") - buf.write("J\u0093K\u0095L\u0097M\u0099\2\u009b\2\u009d\2\u009f\2") - buf.write("\u00a1\2\u00a3\2\u00a5\2\u00a7\2\u00a9\2\u00ab\2\u00ad") - buf.write("\2\u00af\2\u00b1\2\u00b3\2\u00b5\2\u00b7\2\u00b9\2\u00bb") - buf.write("\2\3\2\23\5\2C\\aac|\6\2\62;C\\aac|\5\2\13\f\17\17\"\"") - buf.write("\4\2GGgg\4\2--//\4\2UUuu\4\2FFff\4\2DDdd\4\2QQqq\4\2J") - buf.write("Jjj\3\2\63;\3\2\62;\3\2\62\63\3\2\629\5\2\62;CHch\4\2") - buf.write("ZZzz\5\2AA\\\\||\2\u02b4\2\3\3\2\2\2\2\5\3\2\2\2\2\7\3") - buf.write("\2\2\2\2\t\3\2\2\2\2\13\3\2\2\2\2\r\3\2\2\2\2\17\3\2\2") - buf.write("\2\2\21\3\2\2\2\2\23\3\2\2\2\2\25\3\2\2\2\2\27\3\2\2\2") - buf.write("\2\31\3\2\2\2\2\33\3\2\2\2\2\35\3\2\2\2\2\37\3\2\2\2\2") - buf.write("!\3\2\2\2\2#\3\2\2\2\2%\3\2\2\2\2\'\3\2\2\2\2)\3\2\2\2") - buf.write("\2+\3\2\2\2\2-\3\2\2\2\2/\3\2\2\2\2\61\3\2\2\2\2\63\3") - buf.write("\2\2\2\2\65\3\2\2\2\2\67\3\2\2\2\29\3\2\2\2\2;\3\2\2\2") - buf.write("\2=\3\2\2\2\2?\3\2\2\2\2A\3\2\2\2\2C\3\2\2\2\2E\3\2\2") - buf.write("\2\2G\3\2\2\2\2I\3\2\2\2\2K\3\2\2\2\2M\3\2\2\2\2O\3\2") - buf.write("\2\2\2Q\3\2\2\2\2S\3\2\2\2\2U\3\2\2\2\2W\3\2\2\2\2Y\3") - buf.write("\2\2\2\2[\3\2\2\2\2]\3\2\2\2\2_\3\2\2\2\2a\3\2\2\2\2c") - buf.write("\3\2\2\2\2e\3\2\2\2\2g\3\2\2\2\2i\3\2\2\2\2k\3\2\2\2\2") - buf.write("m\3\2\2\2\2o\3\2\2\2\2q\3\2\2\2\2s\3\2\2\2\2u\3\2\2\2") - buf.write("\2w\3\2\2\2\2y\3\2\2\2\2{\3\2\2\2\2}\3\2\2\2\2\177\3\2") - buf.write("\2\2\2\u0081\3\2\2\2\2\u0083\3\2\2\2\2\u0085\3\2\2\2\2") - buf.write("\u0087\3\2\2\2\2\u0089\3\2\2\2\2\u008b\3\2\2\2\2\u008d") - buf.write("\3\2\2\2\2\u008f\3\2\2\2\2\u0091\3\2\2\2\2\u0093\3\2\2") - buf.write("\2\2\u0095\3\2\2\2\2\u0097\3\2\2\2\3\u00bd\3\2\2\2\5\u00c7") - buf.write("\3\2\2\2\7\u00ce\3\2\2\2\t\u00d0\3\2\2\2\13\u00d2\3\2") - buf.write("\2\2\r\u00d4\3\2\2\2\17\u00d6\3\2\2\2\21\u00dd\3\2\2\2") - buf.write("\23\u00e5\3\2\2\2\25\u00ed\3\2\2\2\27\u00f9\3\2\2\2\31") - buf.write("\u00ff\3\2\2\2\33\u0103\3\2\2\2\35\u0106\3\2\2\2\37\u010b") - buf.write("\3\2\2\2!\u0113\3\2\2\2#\u0118\3\2\2\2%\u011a\3\2\2\2") - buf.write("\'\u0122\3\2\2\2)\u0125\3\2\2\2+\u0127\3\2\2\2-\u012e") - buf.write("\3\2\2\2/\u0133\3\2\2\2\61\u013b\3\2\2\2\63\u0140\3\2") - buf.write("\2\2\65\u0147\3\2\2\2\67\u014e\3\2\2\29\u0150\3\2\2\2") - buf.write(";\u0152\3\2\2\2=\u0154\3\2\2\2?\u0156\3\2\2\2A\u0158\3") - buf.write("\2\2\2C\u015d\3\2\2\2E\u015f\3\2\2\2G\u0169\3\2\2\2I\u0174") - buf.write("\3\2\2\2K\u0178\3\2\2\2M\u017b\3\2\2\2O\u017d\3\2\2\2") - buf.write("Q\u017f\3\2\2\2S\u0182\3\2\2\2U\u0184\3\2\2\2W\u0186\3") - buf.write("\2\2\2Y\u0188\3\2\2\2[\u018a\3\2\2\2]\u018c\3\2\2\2_\u018f") - buf.write("\3\2\2\2a\u0191\3\2\2\2c\u0194\3\2\2\2e\u0196\3\2\2\2") - buf.write("g\u0199\3\2\2\2i\u019c\3\2\2\2k\u019e\3\2\2\2m\u01a0\3") - buf.write("\2\2\2o\u01a2\3\2\2\2q\u01a5\3\2\2\2s\u01a8\3\2\2\2u\u01ac") - buf.write("\3\2\2\2w\u01b0\3\2\2\2y\u01b3\3\2\2\2{\u01b6\3\2\2\2") - buf.write("}\u01b9\3\2\2\2\177\u01bc\3\2\2\2\u0081\u01bf\3\2\2\2") - buf.write("\u0083\u01c3\3\2\2\2\u0085\u01c7\3\2\2\2\u0087\u01cf\3") - buf.write("\2\2\2\u0089\u01d5\3\2\2\2\u008b\u01e5\3\2\2\2\u008d\u01f3") - buf.write("\3\2\2\2\u008f\u0210\3\2\2\2\u0091\u022f\3\2\2\2\u0093") - buf.write("\u0232\3\2\2\2\u0095\u0238\3\2\2\2\u0097\u023e\3\2\2\2") - buf.write("\u0099\u0243\3\2\2\2\u009b\u0245\3\2\2\2\u009d\u0247\3") - buf.write("\2\2\2\u009f\u024f\3\2\2\2\u00a1\u0257\3\2\2\2\u00a3\u025f") - buf.write("\3\2\2\2\u00a5\u0267\3\2\2\2\u00a7\u026f\3\2\2\2\u00a9") - buf.write("\u0275\3\2\2\2\u00ab\u027b\3\2\2\2\u00ad\u0281\3\2\2\2") - buf.write("\u00af\u0287\3\2\2\2\u00b1\u0289\3\2\2\2\u00b3\u028e\3") - buf.write("\2\2\2\u00b5\u0293\3\2\2\2\u00b7\u0298\3\2\2\2\u00b9\u029a") - buf.write("\3\2\2\2\u00bb\u029c\3\2\2\2\u00bd\u00be\7g\2\2\u00be") - buf.write("\u00bf\7p\2\2\u00bf\u00c0\7f\2\2\u00c0\u00c1\7o\2\2\u00c1") - buf.write("\u00c2\7q\2\2\u00c2\u00c3\7f\2\2\u00c3\u00c4\7w\2\2\u00c4") - buf.write("\u00c5\7n\2\2\u00c5\u00c6\7g\2\2\u00c6\4\3\2\2\2\u00c7") - buf.write("\u00c8\7o\2\2\u00c8\u00c9\7q\2\2\u00c9\u00ca\7f\2\2\u00ca") - buf.write("\u00cb\7w\2\2\u00cb\u00cc\7n\2\2\u00cc\u00cd\7g\2\2\u00cd") - buf.write("\6\3\2\2\2\u00ce\u00cf\7*\2\2\u00cf\b\3\2\2\2\u00d0\u00d1") - buf.write("\7+\2\2\u00d1\n\3\2\2\2\u00d2\u00d3\7=\2\2\u00d3\f\3\2") - buf.write("\2\2\u00d4\u00d5\7.\2\2\u00d5\16\3\2\2\2\u00d6\u00d7\7") - buf.write("h\2\2\u00d7\u00d8\7k\2\2\u00d8\u00d9\7p\2\2\u00d9\u00da") - buf.write("\7r\2\2\u00da\u00db\7w\2\2\u00db\u00dc\7v\2\2\u00dc\20") - buf.write("\3\2\2\2\u00dd\u00de\7h\2\2\u00de\u00df\7q\2\2\u00df\u00e0") - buf.write("\7w\2\2\u00e0\u00e1\7v\2\2\u00e1\u00e2\7r\2\2\u00e2\u00e3") - buf.write("\7w\2\2\u00e3\u00e4\7v\2\2\u00e4\22\3\2\2\2\u00e5\u00e6") - buf.write("\7e\2\2\u00e6\u00e7\7q\2\2\u00e7\u00e8\7p\2\2\u00e8\u00e9") - buf.write("\7v\2\2\u00e9\u00ea\7t\2\2\u00ea\u00eb\7q\2\2\u00eb\u00ec") - buf.write("\7n\2\2\u00ec\24\3\2\2\2\u00ed\u00ee\7f\2\2\u00ee\u00ef") - buf.write("\7k\2\2\u00ef\u00f0\7u\2\2\u00f0\u00f1\7v\2\2\u00f1\u00f2") - buf.write("\7t\2\2\u00f2\u00f3\7k\2\2\u00f3\u00f4\7d\2\2\u00f4\u00f5") - buf.write("\7w\2\2\u00f5\u00f6\7v\2\2\u00f6\u00f7\7g\2\2\u00f7\u00f8") - buf.write("\7B\2\2\u00f8\26\3\2\2\2\u00f9\u00fa\7d\2\2\u00fa\u00fb") - buf.write("\7g\2\2\u00fb\u00fc\7i\2\2\u00fc\u00fd\7k\2\2\u00fd\u00fe") - buf.write("\7p\2\2\u00fe\30\3\2\2\2\u00ff\u0100\7g\2\2\u0100\u0101") - buf.write("\7p\2\2\u0101\u0102\7f\2\2\u0102\32\3\2\2\2\u0103\u0104") - buf.write("\7k\2\2\u0104\u0105\7h\2\2\u0105\34\3\2\2\2\u0106\u0107") - buf.write("\7g\2\2\u0107\u0108\7n\2\2\u0108\u0109\7u\2\2\u0109\u010a") - buf.write("\7g\2\2\u010a\36\3\2\2\2\u010b\u010c\7g\2\2\u010c\u010d") - buf.write("\7p\2\2\u010d\u010e\7f\2\2\u010e\u010f\7e\2\2\u010f\u0110") - buf.write("\7c\2\2\u0110\u0111\7u\2\2\u0111\u0112\7g\2\2\u0112 \3") - buf.write("\2\2\2\u0113\u0114\7e\2\2\u0114\u0115\7c\2\2\u0115\u0116") - buf.write("\7u\2\2\u0116\u0117\7g\2\2\u0117\"\3\2\2\2\u0118\u0119") - buf.write("\7<\2\2\u0119$\3\2\2\2\u011a\u011b\7f\2\2\u011b\u011c") - buf.write("\7g\2\2\u011c\u011d\7h\2\2\u011d\u011e\7c\2\2\u011e\u011f") - buf.write("\7w\2\2\u011f\u0120\7n\2\2\u0120\u0121\7v\2\2\u0121&\3") - buf.write("\2\2\2\u0122\u0123\7>\2\2\u0123\u0124\7?\2\2\u0124(\3") - buf.write("\2\2\2\u0125\u0126\7\60\2\2\u0126*\3\2\2\2\u0127\u0128") - buf.write("\7u\2\2\u0128\u0129\7k\2\2\u0129\u012a\7i\2\2\u012a\u012b") - buf.write("\7p\2\2\u012b\u012c\7c\2\2\u012c\u012d\7n\2\2\u012d,\3") - buf.write("\2\2\2\u012e\u012f\7h\2\2\u012f\u0130\7n\2\2\u0130\u0131") - buf.write("\7q\2\2\u0131\u0132\7y\2\2\u0132.\3\2\2\2\u0133\u0134") - buf.write("\7u\2\2\u0134\u0135\7v\2\2\u0135\u0136\7q\2\2\u0136\u0137") - buf.write("\7t\2\2\u0137\u0138\7c\2\2\u0138\u0139\7i\2\2\u0139\u013a") - buf.write("\7g\2\2\u013a\60\3\2\2\2\u013b\u013c\7r\2\2\u013c\u013d") - buf.write("\7w\2\2\u013d\u013e\7o\2\2\u013e\u013f\7r\2\2\u013f\62") - buf.write("\3\2\2\2\u0140\u0141\7p\2\2\u0141\u0142\7w\2\2\u0142\u0143") - buf.write("\7o\2\2\u0143\u0144\7d\2\2\u0144\u0145\7g\2\2\u0145\u0146") - buf.write("\7t\2\2\u0146\64\3\2\2\2\u0147\u0148\7c\2\2\u0148\u0149") - buf.write("\7u\2\2\u0149\u014a\7u\2\2\u014a\u014b\7k\2\2\u014b\u014c") - buf.write("\7i\2\2\u014c\u014d\7p\2\2\u014d\66\3\2\2\2\u014e\u014f") - buf.write("\7?\2\2\u014f8\3\2\2\2\u0150\u0151\7]\2\2\u0151:\3\2\2") - buf.write("\2\u0152\u0153\7_\2\2\u0153<\3\2\2\2\u0154\u0155\7}\2") - buf.write("\2\u0155>\3\2\2\2\u0156\u0157\7\177\2\2\u0157@\3\2\2\2") - buf.write("\u0158\u0159\7%\2\2\u0159\u015a\7O\2\2\u015a\u015b\7C") - buf.write("\2\2\u015b\u015c\7R\2\2\u015cB\3\2\2\2\u015d\u015e\7$") - buf.write("\2\2\u015eD\3\2\2\2\u015f\u0160\7%\2\2\u0160\u0161\7O") - buf.write("\2\2\u0161\u0162\7C\2\2\u0162\u0163\7V\2\2\u0163\u0164") - buf.write("\7G\2\2\u0164\u0165\7T\2\2\u0165\u0166\7K\2\2\u0166\u0167") - buf.write("\7C\2\2\u0167\u0168\7N\2\2\u0168F\3\2\2\2\u0169\u016a") - buf.write("\7%\2\2\u016a\u016b\7E\2\2\u016b\u016c\7Q\2\2\u016c\u016d") - buf.write("\7P\2\2\u016d\u016e\7U\2\2\u016e\u016f\7V\2\2\u016f\u0170") - buf.write("\7T\2\2\u0170\u0171\7C\2\2\u0171\u0172\7K\2\2\u0172\u0173") - buf.write("\7P\2\2\u0173H\3\2\2\2\u0174\u0175\7C\2\2\u0175\u0176") - buf.write("\7P\2\2\u0176\u0177\7F\2\2\u0177J\3\2\2\2\u0178\u0179") - buf.write("\7Q\2\2\u0179\u017a\7T\2\2\u017aL\3\2\2\2\u017b\u017c") - buf.write("\7@\2\2\u017cN\3\2\2\2\u017d\u017e\7>\2\2\u017eP\3\2\2") - buf.write("\2\u017f\u0180\7@\2\2\u0180\u0181\7?\2\2\u0181R\3\2\2") - buf.write("\2\u0182\u0183\7-\2\2\u0183T\3\2\2\2\u0184\u0185\7/\2") - buf.write("\2\u0185V\3\2\2\2\u0186\u0187\7#\2\2\u0187X\3\2\2\2\u0188") - buf.write("\u0189\7\u0080\2\2\u0189Z\3\2\2\2\u018a\u018b\7(\2\2\u018b") - buf.write("\\\3\2\2\2\u018c\u018d\7\u0080\2\2\u018d\u018e\7(\2\2") - buf.write("\u018e^\3\2\2\2\u018f\u0190\7~\2\2\u0190`\3\2\2\2\u0191") - buf.write("\u0192\7\u0080\2\2\u0192\u0193\7~\2\2\u0193b\3\2\2\2\u0194") - buf.write("\u0195\7`\2\2\u0195d\3\2\2\2\u0196\u0197\7\u0080\2\2\u0197") - buf.write("\u0198\7`\2\2\u0198f\3\2\2\2\u0199\u019a\7`\2\2\u019a") - buf.write("\u019b\7\u0080\2\2\u019bh\3\2\2\2\u019c\u019d\7,\2\2\u019d") - buf.write("j\3\2\2\2\u019e\u019f\7\61\2\2\u019fl\3\2\2\2\u01a0\u01a1") - buf.write("\7\'\2\2\u01a1n\3\2\2\2\u01a2\u01a3\7?\2\2\u01a3\u01a4") - buf.write("\7?\2\2\u01a4p\3\2\2\2\u01a5\u01a6\7#\2\2\u01a6\u01a7") - buf.write("\7?\2\2\u01a7r\3\2\2\2\u01a8\u01a9\7?\2\2\u01a9\u01aa") - buf.write("\7?\2\2\u01aa\u01ab\7?\2\2\u01abt\3\2\2\2\u01ac\u01ad") - buf.write("\7#\2\2\u01ad\u01ae\7?\2\2\u01ae\u01af\7?\2\2\u01afv\3") - buf.write("\2\2\2\u01b0\u01b1\7(\2\2\u01b1\u01b2\7(\2\2\u01b2x\3") - buf.write("\2\2\2\u01b3\u01b4\7~\2\2\u01b4\u01b5\7~\2\2\u01b5z\3") - buf.write("\2\2\2\u01b6\u01b7\7,\2\2\u01b7\u01b8\7,\2\2\u01b8|\3") - buf.write("\2\2\2\u01b9\u01ba\7@\2\2\u01ba\u01bb\7@\2\2\u01bb~\3") - buf.write("\2\2\2\u01bc\u01bd\7>\2\2\u01bd\u01be\7>\2\2\u01be\u0080") - buf.write("\3\2\2\2\u01bf\u01c0\7@\2\2\u01c0\u01c1\7@\2\2\u01c1\u01c2") - buf.write("\7@\2\2\u01c2\u0082\3\2\2\2\u01c3\u01c4\7>\2\2\u01c4\u01c5") - buf.write("\7>\2\2\u01c5\u01c6\7>\2\2\u01c6\u0084\3\2\2\2\u01c7\u01cb") - buf.write("\t\2\2\2\u01c8\u01ca\t\3\2\2\u01c9\u01c8\3\2\2\2\u01ca") - buf.write("\u01cd\3\2\2\2\u01cb\u01c9\3\2\2\2\u01cb\u01cc\3\2\2\2") - buf.write("\u01cc\u0086\3\2\2\2\u01cd\u01cb\3\2\2\2\u01ce\u01d0\t") - buf.write("\4\2\2\u01cf\u01ce\3\2\2\2\u01d0\u01d1\3\2\2\2\u01d1\u01cf") - buf.write("\3\2\2\2\u01d1\u01d2\3\2\2\2\u01d2\u01d3\3\2\2\2\u01d3") - buf.write("\u01d4\bD\2\2\u01d4\u0088\3\2\2\2\u01d5\u01d6\7\61\2\2") - buf.write("\u01d6\u01d7\7\61\2\2\u01d7\u01db\3\2\2\2\u01d8\u01da") - buf.write("\13\2\2\2\u01d9\u01d8\3\2\2\2\u01da\u01dd\3\2\2\2\u01db") - buf.write("\u01dc\3\2\2\2\u01db\u01d9\3\2\2\2\u01dc\u01df\3\2\2\2") - buf.write("\u01dd\u01db\3\2\2\2\u01de\u01e0\7\17\2\2\u01df\u01de") - buf.write("\3\2\2\2\u01df\u01e0\3\2\2\2\u01e0\u01e1\3\2\2\2\u01e1") - buf.write("\u01e2\7\f\2\2\u01e2\u01e3\3\2\2\2\u01e3\u01e4\bE\3\2") - buf.write("\u01e4\u008a\3\2\2\2\u01e5\u01e6\7\61\2\2\u01e6\u01e7") - buf.write("\7,\2\2\u01e7\u01eb\3\2\2\2\u01e8\u01ea\13\2\2\2\u01e9") - buf.write("\u01e8\3\2\2\2\u01ea\u01ed\3\2\2\2\u01eb\u01ec\3\2\2\2") - buf.write("\u01eb\u01e9\3\2\2\2\u01ec\u01ee\3\2\2\2\u01ed\u01eb\3") - buf.write("\2\2\2\u01ee\u01ef\7,\2\2\u01ef\u01f0\7\61\2\2\u01f0\u01f1") - buf.write("\3\2\2\2\u01f1\u01f2\bF\3\2\u01f2\u008c\3\2\2\2\u01f3") - buf.write("\u01f7\7b\2\2\u01f4\u01f6\13\2\2\2\u01f5\u01f4\3\2\2\2") - buf.write("\u01f6\u01f9\3\2\2\2\u01f7\u01f8\3\2\2\2\u01f7\u01f5\3") - buf.write("\2\2\2\u01f8\u01fb\3\2\2\2\u01f9\u01f7\3\2\2\2\u01fa\u01fc") - buf.write("\7\17\2\2\u01fb\u01fa\3\2\2\2\u01fb\u01fc\3\2\2\2\u01fc") - buf.write("\u01fd\3\2\2\2\u01fd\u01fe\7\f\2\2\u01fe\u01ff\3\2\2\2") - buf.write("\u01ff\u0200\bG\3\2\u0200\u008e\3\2\2\2\u0201\u0202\5") - buf.write("\u009fP\2\u0202\u0203\7\60\2\2\u0203\u0204\5\u009fP\2") - buf.write("\u0204\u0211\3\2\2\2\u0205\u0208\5\u009fP\2\u0206\u0207") - buf.write("\7\60\2\2\u0207\u0209\5\u009fP\2\u0208\u0206\3\2\2\2\u0208") - buf.write("\u0209\3\2\2\2\u0209\u020a\3\2\2\2\u020a\u020c\t\5\2\2") - buf.write("\u020b\u020d\t\6\2\2\u020c\u020b\3\2\2\2\u020c\u020d\3") - buf.write("\2\2\2\u020d\u020e\3\2\2\2\u020e\u020f\5\u009fP\2\u020f") - buf.write("\u0211\3\2\2\2\u0210\u0201\3\2\2\2\u0210\u0205\3\2\2\2") - buf.write("\u0211\u0090\3\2\2\2\u0212\u0230\5\u009fP\2\u0213\u0215") - buf.write("\5\u009bN\2\u0214\u0213\3\2\2\2\u0214\u0215\3\2\2\2\u0215") - buf.write("\u0216\3\2\2\2\u0216\u0217\5\u00a7T\2\u0217\u0218\5\u009f") - buf.write("P\2\u0218\u0230\3\2\2\2\u0219\u021b\5\u009bN\2\u021a\u0219") - buf.write("\3\2\2\2\u021a\u021b\3\2\2\2\u021b\u021c\3\2\2\2\u021c") - buf.write("\u021d\5\u00a7T\2\u021d\u0221\5\u00b9]\2\u021e\u0220\7") - buf.write("a\2\2\u021f\u021e\3\2\2\2\u0220\u0223\3\2\2\2\u0221\u021f") - buf.write("\3\2\2\2\u0221\u0222\3\2\2\2\u0222\u0230\3\2\2\2\u0223") - buf.write("\u0221\3\2\2\2\u0224\u0226\5\u009bN\2\u0225\u0224\3\2") - buf.write("\2\2\u0225\u0226\3\2\2\2\u0226\u0227\3\2\2\2\u0227\u0228") - buf.write("\5\u00a7T\2\u0228\u022c\5\u00bb^\2\u0229\u022b\7a\2\2") - buf.write("\u022a\u0229\3\2\2\2\u022b\u022e\3\2\2\2\u022c\u022a\3") - buf.write("\2\2\2\u022c\u022d\3\2\2\2\u022d\u0230\3\2\2\2\u022e\u022c") - buf.write("\3\2\2\2\u022f\u0212\3\2\2\2\u022f\u0214\3\2\2\2\u022f") - buf.write("\u021a\3\2\2\2\u022f\u0225\3\2\2\2\u0230\u0092\3\2\2\2") - buf.write("\u0231\u0233\5\u009bN\2\u0232\u0231\3\2\2\2\u0232\u0233") - buf.write("\3\2\2\2\u0233\u0234\3\2\2\2\u0234\u0235\5\u00a9U\2\u0235") - buf.write("\u0236\5\u00a1Q\2\u0236\u0094\3\2\2\2\u0237\u0239\5\u009b") - buf.write("N\2\u0238\u0237\3\2\2\2\u0238\u0239\3\2\2\2\u0239\u023a") - buf.write("\3\2\2\2\u023a\u023b\5\u00abV\2\u023b\u023c\5\u00a3R\2") - buf.write("\u023c\u0096\3\2\2\2\u023d\u023f\5\u009bN\2\u023e\u023d") - buf.write("\3\2\2\2\u023e\u023f\3\2\2\2\u023f\u0240\3\2\2\2\u0240") - buf.write("\u0241\5\u00adW\2\u0241\u0242\5\u00a5S\2\u0242\u0098\3") - buf.write("\2\2\2\u0243\u0244\t\6\2\2\u0244\u009a\3\2\2\2\u0245\u0246") - buf.write("\5\u009dO\2\u0246\u009c\3\2\2\2\u0247\u024c\5\u00afX\2") - buf.write("\u0248\u024b\7a\2\2\u0249\u024b\5\u00b1Y\2\u024a\u0248") - buf.write("\3\2\2\2\u024a\u0249\3\2\2\2\u024b\u024e\3\2\2\2\u024c") - buf.write("\u024a\3\2\2\2\u024c\u024d\3\2\2\2\u024d\u009e\3\2\2\2") - buf.write("\u024e\u024c\3\2\2\2\u024f\u0254\5\u00b1Y\2\u0250\u0253") - buf.write("\7a\2\2\u0251\u0253\5\u00b1Y\2\u0252\u0250\3\2\2\2\u0252") - buf.write("\u0251\3\2\2\2\u0253\u0256\3\2\2\2\u0254\u0252\3\2\2\2") - buf.write("\u0254\u0255\3\2\2\2\u0255\u00a0\3\2\2\2\u0256\u0254\3") - buf.write("\2\2\2\u0257\u025c\5\u00b3Z\2\u0258\u025b\7a\2\2\u0259") - buf.write("\u025b\5\u00b3Z\2\u025a\u0258\3\2\2\2\u025a\u0259\3\2") - buf.write("\2\2\u025b\u025e\3\2\2\2\u025c\u025a\3\2\2\2\u025c\u025d") - buf.write("\3\2\2\2\u025d\u00a2\3\2\2\2\u025e\u025c\3\2\2\2\u025f") - buf.write("\u0264\5\u00b5[\2\u0260\u0263\7a\2\2\u0261\u0263\5\u00b5") - buf.write("[\2\u0262\u0260\3\2\2\2\u0262\u0261\3\2\2\2\u0263\u0266") - buf.write("\3\2\2\2\u0264\u0262\3\2\2\2\u0264\u0265\3\2\2\2\u0265") - buf.write("\u00a4\3\2\2\2\u0266\u0264\3\2\2\2\u0267\u026c\5\u00b7") - buf.write("\\\2\u0268\u026b\7a\2\2\u0269\u026b\5\u00b7\\\2\u026a") - buf.write("\u0268\3\2\2\2\u026a\u0269\3\2\2\2\u026b\u026e\3\2\2\2") - buf.write("\u026c\u026a\3\2\2\2\u026c\u026d\3\2\2\2\u026d\u00a6\3") - buf.write("\2\2\2\u026e\u026c\3\2\2\2\u026f\u0271\7)\2\2\u0270\u0272") - buf.write("\t\7\2\2\u0271\u0270\3\2\2\2\u0271\u0272\3\2\2\2\u0272") - buf.write("\u0273\3\2\2\2\u0273\u0274\t\b\2\2\u0274\u00a8\3\2\2\2") - buf.write("\u0275\u0277\7)\2\2\u0276\u0278\t\7\2\2\u0277\u0276\3") - buf.write("\2\2\2\u0277\u0278\3\2\2\2\u0278\u0279\3\2\2\2\u0279\u027a") - buf.write("\t\t\2\2\u027a\u00aa\3\2\2\2\u027b\u027d\7)\2\2\u027c") - buf.write("\u027e\t\7\2\2\u027d\u027c\3\2\2\2\u027d\u027e\3\2\2\2") - buf.write("\u027e\u027f\3\2\2\2\u027f\u0280\t\n\2\2\u0280\u00ac\3") - buf.write("\2\2\2\u0281\u0283\7)\2\2\u0282\u0284\t\7\2\2\u0283\u0282") - buf.write("\3\2\2\2\u0283\u0284\3\2\2\2\u0284\u0285\3\2\2\2\u0285") - buf.write("\u0286\t\13\2\2\u0286\u00ae\3\2\2\2\u0287\u0288\t\f\2") - buf.write("\2\u0288\u00b0\3\2\2\2\u0289\u028a\t\r\2\2\u028a\u00b2") - buf.write("\3\2\2\2\u028b\u028f\5\u00b9]\2\u028c\u028f\5\u00bb^\2") - buf.write("\u028d\u028f\t\16\2\2\u028e\u028b\3\2\2\2\u028e\u028c") - buf.write("\3\2\2\2\u028e\u028d\3\2\2\2\u028f\u00b4\3\2\2\2\u0290") - buf.write("\u0294\5\u00b9]\2\u0291\u0294\5\u00bb^\2\u0292\u0294\t") - buf.write("\17\2\2\u0293\u0290\3\2\2\2\u0293\u0291\3\2\2\2\u0293") - buf.write("\u0292\3\2\2\2\u0294\u00b6\3\2\2\2\u0295\u0299\5\u00b9") - buf.write("]\2\u0296\u0299\5\u00bb^\2\u0297\u0299\t\20\2\2\u0298") - buf.write("\u0295\3\2\2\2\u0298\u0296\3\2\2\2\u0298\u0297\3\2\2\2") - buf.write("\u0299\u00b8\3\2\2\2\u029a\u029b\t\21\2\2\u029b\u00ba") - buf.write("\3\2\2\2\u029c\u029d\t\22\2\2\u029d\u00bc\3\2\2\2\'\2") - buf.write("\u01cb\u01d1\u01db\u01df\u01eb\u01f7\u01fb\u0208\u020c") - buf.write("\u0210\u0214\u021a\u0221\u0225\u022c\u022f\u0232\u0238") - buf.write("\u023e\u024a\u024c\u0252\u0254\u025a\u025c\u0262\u0264") - buf.write("\u026a\u026c\u0271\u0277\u027d\u0283\u028e\u0293\u0298") - buf.write("\4\b\2\2\2\3\2") - return buf.getvalue() + return [ + 4, + 0, + 75, + 668, + 6, + -1, + 2, + 0, + 7, + 0, + 2, + 1, + 7, + 1, + 2, + 2, + 7, + 2, + 2, + 3, + 7, + 3, + 2, + 4, + 7, + 4, + 2, + 5, + 7, + 5, + 2, + 6, + 7, + 6, + 2, + 7, + 7, + 7, + 2, + 8, + 7, + 8, + 2, + 9, + 7, + 9, + 2, + 10, + 7, + 10, + 2, + 11, + 7, + 11, + 2, + 12, + 7, + 12, + 2, + 13, + 7, + 13, + 2, + 14, + 7, + 14, + 2, + 15, + 7, + 15, + 2, + 16, + 7, + 16, + 2, + 17, + 7, + 17, + 2, + 18, + 7, + 18, + 2, + 19, + 7, + 19, + 2, + 20, + 7, + 20, + 2, + 21, + 7, + 21, + 2, + 22, + 7, + 22, + 2, + 23, + 7, + 23, + 2, + 24, + 7, + 24, + 2, + 25, + 7, + 25, + 2, + 26, + 7, + 26, + 2, + 27, + 7, + 27, + 2, + 28, + 7, + 28, + 2, + 29, + 7, + 29, + 2, + 30, + 7, + 30, + 2, + 31, + 7, + 31, + 2, + 32, + 7, + 32, + 2, + 33, + 7, + 33, + 2, + 34, + 7, + 34, + 2, + 35, + 7, + 35, + 2, + 36, + 7, + 36, + 2, + 37, + 7, + 37, + 2, + 38, + 7, + 38, + 2, + 39, + 7, + 39, + 2, + 40, + 7, + 40, + 2, + 41, + 7, + 41, + 2, + 42, + 7, + 42, + 2, + 43, + 7, + 43, + 2, + 44, + 7, + 44, + 2, + 45, + 7, + 45, + 2, + 46, + 7, + 46, + 2, + 47, + 7, + 47, + 2, + 48, + 7, + 48, + 2, + 49, + 7, + 49, + 2, + 50, + 7, + 50, + 2, + 51, + 7, + 51, + 2, + 52, + 7, + 52, + 2, + 53, + 7, + 53, + 2, + 54, + 7, + 54, + 2, + 55, + 7, + 55, + 2, + 56, + 7, + 56, + 2, + 57, + 7, + 57, + 2, + 58, + 7, + 58, + 2, + 59, + 7, + 59, + 2, + 60, + 7, + 60, + 2, + 61, + 7, + 61, + 2, + 62, + 7, + 62, + 2, + 63, + 7, + 63, + 2, + 64, + 7, + 64, + 2, + 65, + 7, + 65, + 2, + 66, + 7, + 66, + 2, + 67, + 7, + 67, + 2, + 68, + 7, + 68, + 2, + 69, + 7, + 69, + 2, + 70, + 7, + 70, + 2, + 71, + 7, + 71, + 2, + 72, + 7, + 72, + 2, + 73, + 7, + 73, + 2, + 74, + 7, + 74, + 2, + 75, + 7, + 75, + 2, + 76, + 7, + 76, + 2, + 77, + 7, + 77, + 2, + 78, + 7, + 78, + 2, + 79, + 7, + 79, + 2, + 80, + 7, + 80, + 2, + 81, + 7, + 81, + 2, + 82, + 7, + 82, + 2, + 83, + 7, + 83, + 2, + 84, + 7, + 84, + 2, + 85, + 7, + 85, + 2, + 86, + 7, + 86, + 2, + 87, + 7, + 87, + 2, + 88, + 7, + 88, + 2, + 89, + 7, + 89, + 2, + 90, + 7, + 90, + 2, + 91, + 7, + 91, + 2, + 92, + 7, + 92, + 1, + 0, + 1, + 0, + 1, + 0, + 1, + 0, + 1, + 0, + 1, + 0, + 1, + 0, + 1, + 0, + 1, + 0, + 1, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 2, + 1, + 3, + 1, + 3, + 1, + 4, + 1, + 4, + 1, + 5, + 1, + 5, + 1, + 6, + 1, + 6, + 1, + 6, + 1, + 6, + 1, + 6, + 1, + 6, + 1, + 6, + 1, + 7, + 1, + 7, + 1, + 7, + 1, + 7, + 1, + 7, + 1, + 7, + 1, + 7, + 1, + 7, + 1, + 8, + 1, + 8, + 1, + 8, + 1, + 8, + 1, + 8, + 1, + 8, + 1, + 8, + 1, + 8, + 1, + 9, + 1, + 9, + 1, + 9, + 1, + 9, + 1, + 9, + 1, + 9, + 1, + 9, + 1, + 9, + 1, + 9, + 1, + 9, + 1, + 9, + 1, + 9, + 1, + 10, + 1, + 10, + 1, + 10, + 1, + 10, + 1, + 10, + 1, + 10, + 1, + 11, + 1, + 11, + 1, + 11, + 1, + 11, + 1, + 12, + 1, + 12, + 1, + 12, + 1, + 13, + 1, + 13, + 1, + 13, + 1, + 13, + 1, + 13, + 1, + 14, + 1, + 14, + 1, + 14, + 1, + 14, + 1, + 14, + 1, + 14, + 1, + 14, + 1, + 14, + 1, + 15, + 1, + 15, + 1, + 15, + 1, + 15, + 1, + 15, + 1, + 16, + 1, + 16, + 1, + 17, + 1, + 17, + 1, + 17, + 1, + 17, + 1, + 17, + 1, + 17, + 1, + 17, + 1, + 17, + 1, + 18, + 1, + 18, + 1, + 18, + 1, + 19, + 1, + 19, + 1, + 20, + 1, + 20, + 1, + 20, + 1, + 20, + 1, + 20, + 1, + 20, + 1, + 20, + 1, + 21, + 1, + 21, + 1, + 21, + 1, + 21, + 1, + 21, + 1, + 22, + 1, + 22, + 1, + 22, + 1, + 22, + 1, + 22, + 1, + 22, + 1, + 22, + 1, + 22, + 1, + 23, + 1, + 23, + 1, + 23, + 1, + 23, + 1, + 23, + 1, + 24, + 1, + 24, + 1, + 24, + 1, + 24, + 1, + 24, + 1, + 24, + 1, + 24, + 1, + 25, + 1, + 25, + 1, + 25, + 1, + 25, + 1, + 25, + 1, + 25, + 1, + 25, + 1, + 26, + 1, + 26, + 1, + 27, + 1, + 27, + 1, + 28, + 1, + 28, + 1, + 29, + 1, + 29, + 1, + 30, + 1, + 30, + 1, + 31, + 1, + 31, + 1, + 31, + 1, + 31, + 1, + 31, + 1, + 32, + 1, + 32, + 1, + 33, + 1, + 33, + 1, + 33, + 1, + 33, + 1, + 33, + 1, + 33, + 1, + 33, + 1, + 33, + 1, + 33, + 1, + 33, + 1, + 34, + 1, + 34, + 1, + 34, + 1, + 34, + 1, + 34, + 1, + 34, + 1, + 34, + 1, + 34, + 1, + 34, + 1, + 34, + 1, + 34, + 1, + 35, + 1, + 35, + 1, + 35, + 1, + 35, + 1, + 36, + 1, + 36, + 1, + 36, + 1, + 37, + 1, + 37, + 1, + 38, + 1, + 38, + 1, + 39, + 1, + 39, + 1, + 39, + 1, + 40, + 1, + 40, + 1, + 41, + 1, + 41, + 1, + 42, + 1, + 42, + 1, + 43, + 1, + 43, + 1, + 44, + 1, + 44, + 1, + 45, + 1, + 45, + 1, + 45, + 1, + 46, + 1, + 46, + 1, + 47, + 1, + 47, + 1, + 47, + 1, + 48, + 1, + 48, + 1, + 49, + 1, + 49, + 1, + 49, + 1, + 50, + 1, + 50, + 1, + 50, + 1, + 51, + 1, + 51, + 1, + 52, + 1, + 52, + 1, + 53, + 1, + 53, + 1, + 54, + 1, + 54, + 1, + 54, + 1, + 55, + 1, + 55, + 1, + 55, + 1, + 56, + 1, + 56, + 1, + 56, + 1, + 56, + 1, + 57, + 1, + 57, + 1, + 57, + 1, + 57, + 1, + 58, + 1, + 58, + 1, + 58, + 1, + 59, + 1, + 59, + 1, + 59, + 1, + 60, + 1, + 60, + 1, + 60, + 1, + 61, + 1, + 61, + 1, + 61, + 1, + 62, + 1, + 62, + 1, + 62, + 1, + 63, + 1, + 63, + 1, + 63, + 1, + 63, + 1, + 64, + 1, + 64, + 1, + 64, + 1, + 64, + 1, + 65, + 1, + 65, + 5, + 65, + 456, + 8, + 65, + 10, + 65, + 12, + 65, + 459, + 9, + 65, + 1, + 66, + 4, + 66, + 462, + 8, + 66, + 11, + 66, + 12, + 66, + 463, + 1, + 66, + 1, + 66, + 1, + 67, + 1, + 67, + 1, + 67, + 1, + 67, + 5, + 67, + 472, + 8, + 67, + 10, + 67, + 12, + 67, + 475, + 9, + 67, + 1, + 67, + 3, + 67, + 478, + 8, + 67, + 1, + 67, + 1, + 67, + 1, + 67, + 1, + 67, + 1, + 68, + 1, + 68, + 1, + 68, + 1, + 68, + 5, + 68, + 488, + 8, + 68, + 10, + 68, + 12, + 68, + 491, + 9, + 68, + 1, + 68, + 1, + 68, + 1, + 68, + 1, + 68, + 1, + 68, + 1, + 69, + 1, + 69, + 5, + 69, + 500, + 8, + 69, + 10, + 69, + 12, + 69, + 503, + 9, + 69, + 1, + 69, + 3, + 69, + 506, + 8, + 69, + 1, + 69, + 1, + 69, + 1, + 69, + 1, + 69, + 1, + 70, + 1, + 70, + 1, + 70, + 1, + 70, + 1, + 70, + 1, + 70, + 1, + 70, + 3, + 70, + 519, + 8, + 70, + 1, + 70, + 1, + 70, + 3, + 70, + 523, + 8, + 70, + 1, + 70, + 1, + 70, + 3, + 70, + 527, + 8, + 70, + 1, + 71, + 1, + 71, + 3, + 71, + 531, + 8, + 71, + 1, + 71, + 1, + 71, + 1, + 71, + 1, + 71, + 3, + 71, + 537, + 8, + 71, + 1, + 71, + 1, + 71, + 1, + 71, + 5, + 71, + 542, + 8, + 71, + 10, + 71, + 12, + 71, + 545, + 9, + 71, + 1, + 71, + 3, + 71, + 548, + 8, + 71, + 1, + 71, + 1, + 71, + 1, + 71, + 5, + 71, + 553, + 8, + 71, + 10, + 71, + 12, + 71, + 556, + 9, + 71, + 3, + 71, + 558, + 8, + 71, + 1, + 72, + 3, + 72, + 561, + 8, + 72, + 1, + 72, + 1, + 72, + 1, + 72, + 1, + 73, + 3, + 73, + 567, + 8, + 73, + 1, + 73, + 1, + 73, + 1, + 73, + 1, + 74, + 3, + 74, + 573, + 8, + 74, + 1, + 74, + 1, + 74, + 1, + 74, + 1, + 75, + 1, + 75, + 1, + 76, + 1, + 76, + 1, + 77, + 1, + 77, + 1, + 77, + 5, + 77, + 585, + 8, + 77, + 10, + 77, + 12, + 77, + 588, + 9, + 77, + 1, + 78, + 1, + 78, + 1, + 78, + 5, + 78, + 593, + 8, + 78, + 10, + 78, + 12, + 78, + 596, + 9, + 78, + 1, + 79, + 1, + 79, + 1, + 79, + 5, + 79, + 601, + 8, + 79, + 10, + 79, + 12, + 79, + 604, + 9, + 79, + 1, + 80, + 1, + 80, + 1, + 80, + 5, + 80, + 609, + 8, + 80, + 10, + 80, + 12, + 80, + 612, + 9, + 80, + 1, + 81, + 1, + 81, + 1, + 81, + 5, + 81, + 617, + 8, + 81, + 10, + 81, + 12, + 81, + 620, + 9, + 81, + 1, + 82, + 1, + 82, + 3, + 82, + 624, + 8, + 82, + 1, + 82, + 1, + 82, + 1, + 83, + 1, + 83, + 3, + 83, + 630, + 8, + 83, + 1, + 83, + 1, + 83, + 1, + 84, + 1, + 84, + 3, + 84, + 636, + 8, + 84, + 1, + 84, + 1, + 84, + 1, + 85, + 1, + 85, + 3, + 85, + 642, + 8, + 85, + 1, + 85, + 1, + 85, + 1, + 86, + 1, + 86, + 1, + 87, + 1, + 87, + 1, + 88, + 1, + 88, + 1, + 88, + 3, + 88, + 653, + 8, + 88, + 1, + 89, + 1, + 89, + 1, + 89, + 3, + 89, + 658, + 8, + 89, + 1, + 90, + 1, + 90, + 1, + 90, + 3, + 90, + 663, + 8, + 90, + 1, + 91, + 1, + 91, + 1, + 92, + 1, + 92, + 3, + 473, + 489, + 501, + 0, + 93, + 1, + 1, + 3, + 2, + 5, + 3, + 7, + 4, + 9, + 5, + 11, + 6, + 13, + 7, + 15, + 8, + 17, + 9, + 19, + 10, + 21, + 11, + 23, + 12, + 25, + 13, + 27, + 14, + 29, + 15, + 31, + 16, + 33, + 17, + 35, + 18, + 37, + 19, + 39, + 20, + 41, + 21, + 43, + 22, + 45, + 23, + 47, + 24, + 49, + 25, + 51, + 26, + 53, + 27, + 55, + 28, + 57, + 29, + 59, + 30, + 61, + 31, + 63, + 32, + 65, + 33, + 67, + 34, + 69, + 35, + 71, + 36, + 73, + 37, + 75, + 38, + 77, + 39, + 79, + 40, + 81, + 41, + 83, + 42, + 85, + 43, + 87, + 44, + 89, + 45, + 91, + 46, + 93, + 47, + 95, + 48, + 97, + 49, + 99, + 50, + 101, + 51, + 103, + 52, + 105, + 53, + 107, + 54, + 109, + 55, + 111, + 56, + 113, + 57, + 115, + 58, + 117, + 59, + 119, + 60, + 121, + 61, + 123, + 62, + 125, + 63, + 127, + 64, + 129, + 65, + 131, + 66, + 133, + 67, + 135, + 68, + 137, + 69, + 139, + 70, + 141, + 71, + 143, + 72, + 145, + 73, + 147, + 74, + 149, + 75, + 151, + 0, + 153, + 0, + 155, + 0, + 157, + 0, + 159, + 0, + 161, + 0, + 163, + 0, + 165, + 0, + 167, + 0, + 169, + 0, + 171, + 0, + 173, + 0, + 175, + 0, + 177, + 0, + 179, + 0, + 181, + 0, + 183, + 0, + 185, + 0, + 1, + 0, + 17, + 3, + 0, + 65, + 90, + 95, + 95, + 97, + 122, + 4, + 0, + 48, + 57, + 65, + 90, + 95, + 95, + 97, + 122, + 3, + 0, + 9, + 10, + 13, + 13, + 32, + 32, + 2, + 0, + 69, + 69, + 101, + 101, + 2, + 0, + 43, + 43, + 45, + 45, + 2, + 0, + 83, + 83, + 115, + 115, + 2, + 0, + 68, + 68, + 100, + 100, + 2, + 0, + 66, + 66, + 98, + 98, + 2, + 0, + 79, + 79, + 111, + 111, + 2, + 0, + 72, + 72, + 104, + 104, + 1, + 0, + 49, + 57, + 1, + 0, + 48, + 57, + 1, + 0, + 48, + 49, + 1, + 0, + 48, + 55, + 3, + 0, + 48, + 57, + 65, + 70, + 97, + 102, + 2, + 0, + 88, + 88, + 120, + 120, + 3, + 0, + 63, + 63, + 90, + 90, + 122, + 122, + 690, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 3, + 1, + 0, + 0, + 0, + 0, + 5, + 1, + 0, + 0, + 0, + 0, + 7, + 1, + 0, + 0, + 0, + 0, + 9, + 1, + 0, + 0, + 0, + 0, + 11, + 1, + 0, + 0, + 0, + 0, + 13, + 1, + 0, + 0, + 0, + 0, + 15, + 1, + 0, + 0, + 0, + 0, + 17, + 1, + 0, + 0, + 0, + 0, + 19, + 1, + 0, + 0, + 0, + 0, + 21, + 1, + 0, + 0, + 0, + 0, + 23, + 1, + 0, + 0, + 0, + 0, + 25, + 1, + 0, + 0, + 0, + 0, + 27, + 1, + 0, + 0, + 0, + 0, + 29, + 1, + 0, + 0, + 0, + 0, + 31, + 1, + 0, + 0, + 0, + 0, + 33, + 1, + 0, + 0, + 0, + 0, + 35, + 1, + 0, + 0, + 0, + 0, + 37, + 1, + 0, + 0, + 0, + 0, + 39, + 1, + 0, + 0, + 0, + 0, + 41, + 1, + 0, + 0, + 0, + 0, + 43, + 1, + 0, + 0, + 0, + 0, + 45, + 1, + 0, + 0, + 0, + 0, + 47, + 1, + 0, + 0, + 0, + 0, + 49, + 1, + 0, + 0, + 0, + 0, + 51, + 1, + 0, + 0, + 0, + 0, + 53, + 1, + 0, + 0, + 0, + 0, + 55, + 1, + 0, + 0, + 0, + 0, + 57, + 1, + 0, + 0, + 0, + 0, + 59, + 1, + 0, + 0, + 0, + 0, + 61, + 1, + 0, + 0, + 0, + 0, + 63, + 1, + 0, + 0, + 0, + 0, + 65, + 1, + 0, + 0, + 0, + 0, + 67, + 1, + 0, + 0, + 0, + 0, + 69, + 1, + 0, + 0, + 0, + 0, + 71, + 1, + 0, + 0, + 0, + 0, + 73, + 1, + 0, + 0, + 0, + 0, + 75, + 1, + 0, + 0, + 0, + 0, + 77, + 1, + 0, + 0, + 0, + 0, + 79, + 1, + 0, + 0, + 0, + 0, + 81, + 1, + 0, + 0, + 0, + 0, + 83, + 1, + 0, + 0, + 0, + 0, + 85, + 1, + 0, + 0, + 0, + 0, + 87, + 1, + 0, + 0, + 0, + 0, + 89, + 1, + 0, + 0, + 0, + 0, + 91, + 1, + 0, + 0, + 0, + 0, + 93, + 1, + 0, + 0, + 0, + 0, + 95, + 1, + 0, + 0, + 0, + 0, + 97, + 1, + 0, + 0, + 0, + 0, + 99, + 1, + 0, + 0, + 0, + 0, + 101, + 1, + 0, + 0, + 0, + 0, + 103, + 1, + 0, + 0, + 0, + 0, + 105, + 1, + 0, + 0, + 0, + 0, + 107, + 1, + 0, + 0, + 0, + 0, + 109, + 1, + 0, + 0, + 0, + 0, + 111, + 1, + 0, + 0, + 0, + 0, + 113, + 1, + 0, + 0, + 0, + 0, + 115, + 1, + 0, + 0, + 0, + 0, + 117, + 1, + 0, + 0, + 0, + 0, + 119, + 1, + 0, + 0, + 0, + 0, + 121, + 1, + 0, + 0, + 0, + 0, + 123, + 1, + 0, + 0, + 0, + 0, + 125, + 1, + 0, + 0, + 0, + 0, + 127, + 1, + 0, + 0, + 0, + 0, + 129, + 1, + 0, + 0, + 0, + 0, + 131, + 1, + 0, + 0, + 0, + 0, + 133, + 1, + 0, + 0, + 0, + 0, + 135, + 1, + 0, + 0, + 0, + 0, + 137, + 1, + 0, + 0, + 0, + 0, + 139, + 1, + 0, + 0, + 0, + 0, + 141, + 1, + 0, + 0, + 0, + 0, + 143, + 1, + 0, + 0, + 0, + 0, + 145, + 1, + 0, + 0, + 0, + 0, + 147, + 1, + 0, + 0, + 0, + 0, + 149, + 1, + 0, + 0, + 0, + 1, + 187, + 1, + 0, + 0, + 0, + 3, + 197, + 1, + 0, + 0, + 0, + 5, + 204, + 1, + 0, + 0, + 0, + 7, + 206, + 1, + 0, + 0, + 0, + 9, + 208, + 1, + 0, + 0, + 0, + 11, + 210, + 1, + 0, + 0, + 0, + 13, + 212, + 1, + 0, + 0, + 0, + 15, + 219, + 1, + 0, + 0, + 0, + 17, + 227, + 1, + 0, + 0, + 0, + 19, + 235, + 1, + 0, + 0, + 0, + 21, + 247, + 1, + 0, + 0, + 0, + 23, + 253, + 1, + 0, + 0, + 0, + 25, + 257, + 1, + 0, + 0, + 0, + 27, + 260, + 1, + 0, + 0, + 0, + 29, + 265, + 1, + 0, + 0, + 0, + 31, + 273, + 1, + 0, + 0, + 0, + 33, + 278, + 1, + 0, + 0, + 0, + 35, + 280, + 1, + 0, + 0, + 0, + 37, + 288, + 1, + 0, + 0, + 0, + 39, + 291, + 1, + 0, + 0, + 0, + 41, + 293, + 1, + 0, + 0, + 0, + 43, + 300, + 1, + 0, + 0, + 0, + 45, + 305, + 1, + 0, + 0, + 0, + 47, + 313, + 1, + 0, + 0, + 0, + 49, + 318, + 1, + 0, + 0, + 0, + 51, + 325, + 1, + 0, + 0, + 0, + 53, + 332, + 1, + 0, + 0, + 0, + 55, + 334, + 1, + 0, + 0, + 0, + 57, + 336, + 1, + 0, + 0, + 0, + 59, + 338, + 1, + 0, + 0, + 0, + 61, + 340, + 1, + 0, + 0, + 0, + 63, + 342, + 1, + 0, + 0, + 0, + 65, + 347, + 1, + 0, + 0, + 0, + 67, + 349, + 1, + 0, + 0, + 0, + 69, + 359, + 1, + 0, + 0, + 0, + 71, + 370, + 1, + 0, + 0, + 0, + 73, + 374, + 1, + 0, + 0, + 0, + 75, + 377, + 1, + 0, + 0, + 0, + 77, + 379, + 1, + 0, + 0, + 0, + 79, + 381, + 1, + 0, + 0, + 0, + 81, + 384, + 1, + 0, + 0, + 0, + 83, + 386, + 1, + 0, + 0, + 0, + 85, + 388, + 1, + 0, + 0, + 0, + 87, + 390, + 1, + 0, + 0, + 0, + 89, + 392, + 1, + 0, + 0, + 0, + 91, + 394, + 1, + 0, + 0, + 0, + 93, + 397, + 1, + 0, + 0, + 0, + 95, + 399, + 1, + 0, + 0, + 0, + 97, + 402, + 1, + 0, + 0, + 0, + 99, + 404, + 1, + 0, + 0, + 0, + 101, + 407, + 1, + 0, + 0, + 0, + 103, + 410, + 1, + 0, + 0, + 0, + 105, + 412, + 1, + 0, + 0, + 0, + 107, + 414, + 1, + 0, + 0, + 0, + 109, + 416, + 1, + 0, + 0, + 0, + 111, + 419, + 1, + 0, + 0, + 0, + 113, + 422, + 1, + 0, + 0, + 0, + 115, + 426, + 1, + 0, + 0, + 0, + 117, + 430, + 1, + 0, + 0, + 0, + 119, + 433, + 1, + 0, + 0, + 0, + 121, + 436, + 1, + 0, + 0, + 0, + 123, + 439, + 1, + 0, + 0, + 0, + 125, + 442, + 1, + 0, + 0, + 0, + 127, + 445, + 1, + 0, + 0, + 0, + 129, + 449, + 1, + 0, + 0, + 0, + 131, + 453, + 1, + 0, + 0, + 0, + 133, + 461, + 1, + 0, + 0, + 0, + 135, + 467, + 1, + 0, + 0, + 0, + 137, + 483, + 1, + 0, + 0, + 0, + 139, + 497, + 1, + 0, + 0, + 0, + 141, + 526, + 1, + 0, + 0, + 0, + 143, + 557, + 1, + 0, + 0, + 0, + 145, + 560, + 1, + 0, + 0, + 0, + 147, + 566, + 1, + 0, + 0, + 0, + 149, + 572, + 1, + 0, + 0, + 0, + 151, + 577, + 1, + 0, + 0, + 0, + 153, + 579, + 1, + 0, + 0, + 0, + 155, + 581, + 1, + 0, + 0, + 0, + 157, + 589, + 1, + 0, + 0, + 0, + 159, + 597, + 1, + 0, + 0, + 0, + 161, + 605, + 1, + 0, + 0, + 0, + 163, + 613, + 1, + 0, + 0, + 0, + 165, + 621, + 1, + 0, + 0, + 0, + 167, + 627, + 1, + 0, + 0, + 0, + 169, + 633, + 1, + 0, + 0, + 0, + 171, + 639, + 1, + 0, + 0, + 0, + 173, + 645, + 1, + 0, + 0, + 0, + 175, + 647, + 1, + 0, + 0, + 0, + 177, + 652, + 1, + 0, + 0, + 0, + 179, + 657, + 1, + 0, + 0, + 0, + 181, + 662, + 1, + 0, + 0, + 0, + 183, + 664, + 1, + 0, + 0, + 0, + 185, + 666, + 1, + 0, + 0, + 0, + 187, + 188, + 5, + 101, + 0, + 0, + 188, + 189, + 5, + 110, + 0, + 0, + 189, + 190, + 5, + 100, + 0, + 0, + 190, + 191, + 5, + 109, + 0, + 0, + 191, + 192, + 5, + 111, + 0, + 0, + 192, + 193, + 5, + 100, + 0, + 0, + 193, + 194, + 5, + 117, + 0, + 0, + 194, + 195, + 5, + 108, + 0, + 0, + 195, + 196, + 5, + 101, + 0, + 0, + 196, + 2, + 1, + 0, + 0, + 0, + 197, + 198, + 5, + 109, + 0, + 0, + 198, + 199, + 5, + 111, + 0, + 0, + 199, + 200, + 5, + 100, + 0, + 0, + 200, + 201, + 5, + 117, + 0, + 0, + 201, + 202, + 5, + 108, + 0, + 0, + 202, + 203, + 5, + 101, + 0, + 0, + 203, + 4, + 1, + 0, + 0, + 0, + 204, + 205, + 5, + 40, + 0, + 0, + 205, + 6, + 1, + 0, + 0, + 0, + 206, + 207, + 5, + 41, + 0, + 0, + 207, + 8, + 1, + 0, + 0, + 0, + 208, + 209, + 5, + 59, + 0, + 0, + 209, + 10, + 1, + 0, + 0, + 0, + 210, + 211, + 5, + 44, + 0, + 0, + 211, + 12, + 1, + 0, + 0, + 0, + 212, + 213, + 5, + 102, + 0, + 0, + 213, + 214, + 5, + 105, + 0, + 0, + 214, + 215, + 5, + 110, + 0, + 0, + 215, + 216, + 5, + 112, + 0, + 0, + 216, + 217, + 5, + 117, + 0, + 0, + 217, + 218, + 5, + 116, + 0, + 0, + 218, + 14, + 1, + 0, + 0, + 0, + 219, + 220, + 5, + 102, + 0, + 0, + 220, + 221, + 5, + 111, + 0, + 0, + 221, + 222, + 5, + 117, + 0, + 0, + 222, + 223, + 5, + 116, + 0, + 0, + 223, + 224, + 5, + 112, + 0, + 0, + 224, + 225, + 5, + 117, + 0, + 0, + 225, + 226, + 5, + 116, + 0, + 0, + 226, + 16, + 1, + 0, + 0, + 0, + 227, + 228, + 5, + 99, + 0, + 0, + 228, + 229, + 5, + 111, + 0, + 0, + 229, + 230, + 5, + 110, + 0, + 0, + 230, + 231, + 5, + 116, + 0, + 0, + 231, + 232, + 5, + 114, + 0, + 0, + 232, + 233, + 5, + 111, + 0, + 0, + 233, + 234, + 5, + 108, + 0, + 0, + 234, + 18, + 1, + 0, + 0, + 0, + 235, + 236, + 5, + 100, + 0, + 0, + 236, + 237, + 5, + 105, + 0, + 0, + 237, + 238, + 5, + 115, + 0, + 0, + 238, + 239, + 5, + 116, + 0, + 0, + 239, + 240, + 5, + 114, + 0, + 0, + 240, + 241, + 5, + 105, + 0, + 0, + 241, + 242, + 5, + 98, + 0, + 0, + 242, + 243, + 5, + 117, + 0, + 0, + 243, + 244, + 5, + 116, + 0, + 0, + 244, + 245, + 5, + 101, + 0, + 0, + 245, + 246, + 5, + 64, + 0, + 0, + 246, + 20, + 1, + 0, + 0, + 0, + 247, + 248, + 5, + 98, + 0, + 0, + 248, + 249, + 5, + 101, + 0, + 0, + 249, + 250, + 5, + 103, + 0, + 0, + 250, + 251, + 5, + 105, + 0, + 0, + 251, + 252, + 5, + 110, + 0, + 0, + 252, + 22, + 1, + 0, + 0, + 0, + 253, + 254, + 5, + 101, + 0, + 0, + 254, + 255, + 5, + 110, + 0, + 0, + 255, + 256, + 5, + 100, + 0, + 0, + 256, + 24, + 1, + 0, + 0, + 0, + 257, + 258, + 5, + 105, + 0, + 0, + 258, + 259, + 5, + 102, + 0, + 0, + 259, + 26, + 1, + 0, + 0, + 0, + 260, + 261, + 5, + 101, + 0, + 0, + 261, + 262, + 5, + 108, + 0, + 0, + 262, + 263, + 5, + 115, + 0, + 0, + 263, + 264, + 5, + 101, + 0, + 0, + 264, + 28, + 1, + 0, + 0, + 0, + 265, + 266, + 5, + 101, + 0, + 0, + 266, + 267, + 5, + 110, + 0, + 0, + 267, + 268, + 5, + 100, + 0, + 0, + 268, + 269, + 5, + 99, + 0, + 0, + 269, + 270, + 5, + 97, + 0, + 0, + 270, + 271, + 5, + 115, + 0, + 0, + 271, + 272, + 5, + 101, + 0, + 0, + 272, + 30, + 1, + 0, + 0, + 0, + 273, + 274, + 5, + 99, + 0, + 0, + 274, + 275, + 5, + 97, + 0, + 0, + 275, + 276, + 5, + 115, + 0, + 0, + 276, + 277, + 5, + 101, + 0, + 0, + 277, + 32, + 1, + 0, + 0, + 0, + 278, + 279, + 5, + 58, + 0, + 0, + 279, + 34, + 1, + 0, + 0, + 0, + 280, + 281, + 5, + 100, + 0, + 0, + 281, + 282, + 5, + 101, + 0, + 0, + 282, + 283, + 5, + 102, + 0, + 0, + 283, + 284, + 5, + 97, + 0, + 0, + 284, + 285, + 5, + 117, + 0, + 0, + 285, + 286, + 5, + 108, + 0, + 0, + 286, + 287, + 5, + 116, + 0, + 0, + 287, + 36, + 1, + 0, + 0, + 0, + 288, + 289, + 5, + 60, + 0, + 0, + 289, + 290, + 5, + 61, + 0, + 0, + 290, + 38, + 1, + 0, + 0, + 0, + 291, + 292, + 5, + 46, + 0, + 0, + 292, + 40, + 1, + 0, + 0, + 0, + 293, + 294, + 5, + 115, + 0, + 0, + 294, + 295, + 5, + 105, + 0, + 0, + 295, + 296, + 5, + 103, + 0, + 0, + 296, + 297, + 5, + 110, + 0, + 0, + 297, + 298, + 5, + 97, + 0, + 0, + 298, + 299, + 5, + 108, + 0, + 0, + 299, + 42, + 1, + 0, + 0, + 0, + 300, + 301, + 5, + 102, + 0, + 0, + 301, + 302, + 5, + 108, + 0, + 0, + 302, + 303, + 5, + 111, + 0, + 0, + 303, + 304, + 5, + 119, + 0, + 0, + 304, + 44, + 1, + 0, + 0, + 0, + 305, + 306, + 5, + 115, + 0, + 0, + 306, + 307, + 5, + 116, + 0, + 0, + 307, + 308, + 5, + 111, + 0, + 0, + 308, + 309, + 5, + 114, + 0, + 0, + 309, + 310, + 5, + 97, + 0, + 0, + 310, + 311, + 5, + 103, + 0, + 0, + 311, + 312, + 5, + 101, + 0, + 0, + 312, + 46, + 1, + 0, + 0, + 0, + 313, + 314, + 5, + 112, + 0, + 0, + 314, + 315, + 5, + 117, + 0, + 0, + 315, + 316, + 5, + 109, + 0, + 0, + 316, + 317, + 5, + 112, + 0, + 0, + 317, + 48, + 1, + 0, + 0, + 0, + 318, + 319, + 5, + 110, + 0, + 0, + 319, + 320, + 5, + 117, + 0, + 0, + 320, + 321, + 5, + 109, + 0, + 0, + 321, + 322, + 5, + 98, + 0, + 0, + 322, + 323, + 5, + 101, + 0, + 0, + 323, + 324, + 5, + 114, + 0, + 0, + 324, + 50, + 1, + 0, + 0, + 0, + 325, + 326, + 5, + 97, + 0, + 0, + 326, + 327, + 5, + 115, + 0, + 0, + 327, + 328, + 5, + 115, + 0, + 0, + 328, + 329, + 5, + 105, + 0, + 0, + 329, + 330, + 5, + 103, + 0, + 0, + 330, + 331, + 5, + 110, + 0, + 0, + 331, + 52, + 1, + 0, + 0, + 0, + 332, + 333, + 5, + 61, + 0, + 0, + 333, + 54, + 1, + 0, + 0, + 0, + 334, + 335, + 5, + 91, + 0, + 0, + 335, + 56, + 1, + 0, + 0, + 0, + 336, + 337, + 5, + 93, + 0, + 0, + 337, + 58, + 1, + 0, + 0, + 0, + 338, + 339, + 5, + 123, + 0, + 0, + 339, + 60, + 1, + 0, + 0, + 0, + 340, + 341, + 5, + 125, + 0, + 0, + 341, + 62, + 1, + 0, + 0, + 0, + 342, + 343, + 5, + 35, + 0, + 0, + 343, + 344, + 5, + 77, + 0, + 0, + 344, + 345, + 5, + 65, + 0, + 0, + 345, + 346, + 5, + 80, + 0, + 0, + 346, + 64, + 1, + 0, + 0, + 0, + 347, + 348, + 5, + 34, + 0, + 0, + 348, + 66, + 1, + 0, + 0, + 0, + 349, + 350, + 5, + 35, + 0, + 0, + 350, + 351, + 5, + 77, + 0, + 0, + 351, + 352, + 5, + 65, + 0, + 0, + 352, + 353, + 5, + 84, + 0, + 0, + 353, + 354, + 5, + 69, + 0, + 0, + 354, + 355, + 5, + 82, + 0, + 0, + 355, + 356, + 5, + 73, + 0, + 0, + 356, + 357, + 5, + 65, + 0, + 0, + 357, + 358, + 5, + 76, + 0, + 0, + 358, + 68, + 1, + 0, + 0, + 0, + 359, + 360, + 5, + 35, + 0, + 0, + 360, + 361, + 5, + 67, + 0, + 0, + 361, + 362, + 5, + 79, + 0, + 0, + 362, + 363, + 5, + 78, + 0, + 0, + 363, + 364, + 5, + 83, + 0, + 0, + 364, + 365, + 5, + 84, + 0, + 0, + 365, + 366, + 5, + 82, + 0, + 0, + 366, + 367, + 5, + 65, + 0, + 0, + 367, + 368, + 5, + 73, + 0, + 0, + 368, + 369, + 5, + 78, + 0, + 0, + 369, + 70, + 1, + 0, + 0, + 0, + 370, + 371, + 5, + 65, + 0, + 0, + 371, + 372, + 5, + 78, + 0, + 0, + 372, + 373, + 5, + 68, + 0, + 0, + 373, + 72, + 1, + 0, + 0, + 0, + 374, + 375, + 5, + 79, + 0, + 0, + 375, + 376, + 5, + 82, + 0, + 0, + 376, + 74, + 1, + 0, + 0, + 0, + 377, + 378, + 5, + 62, + 0, + 0, + 378, + 76, + 1, + 0, + 0, + 0, + 379, + 380, + 5, + 60, + 0, + 0, + 380, + 78, + 1, + 0, + 0, + 0, + 381, + 382, + 5, + 62, + 0, + 0, + 382, + 383, + 5, + 61, + 0, + 0, + 383, + 80, + 1, + 0, + 0, + 0, + 384, + 385, + 5, + 43, + 0, + 0, + 385, + 82, + 1, + 0, + 0, + 0, + 386, + 387, + 5, + 45, + 0, + 0, + 387, + 84, + 1, + 0, + 0, + 0, + 388, + 389, + 5, + 33, + 0, + 0, + 389, + 86, + 1, + 0, + 0, + 0, + 390, + 391, + 5, + 126, + 0, + 0, + 391, + 88, + 1, + 0, + 0, + 0, + 392, + 393, + 5, + 38, + 0, + 0, + 393, + 90, + 1, + 0, + 0, + 0, + 394, + 395, + 5, + 126, + 0, + 0, + 395, + 396, + 5, + 38, + 0, + 0, + 396, + 92, + 1, + 0, + 0, + 0, + 397, + 398, + 5, + 124, + 0, + 0, + 398, + 94, + 1, + 0, + 0, + 0, + 399, + 400, + 5, + 126, + 0, + 0, + 400, + 401, + 5, + 124, + 0, + 0, + 401, + 96, + 1, + 0, + 0, + 0, + 402, + 403, + 5, + 94, + 0, + 0, + 403, + 98, + 1, + 0, + 0, + 0, + 404, + 405, + 5, + 126, + 0, + 0, + 405, + 406, + 5, + 94, + 0, + 0, + 406, + 100, + 1, + 0, + 0, + 0, + 407, + 408, + 5, + 94, + 0, + 0, + 408, + 409, + 5, + 126, + 0, + 0, + 409, + 102, + 1, + 0, + 0, + 0, + 410, + 411, + 5, + 42, + 0, + 0, + 411, + 104, + 1, + 0, + 0, + 0, + 412, + 413, + 5, + 47, + 0, + 0, + 413, + 106, + 1, + 0, + 0, + 0, + 414, + 415, + 5, + 37, + 0, + 0, + 415, + 108, + 1, + 0, + 0, + 0, + 416, + 417, + 5, + 61, + 0, + 0, + 417, + 418, + 5, + 61, + 0, + 0, + 418, + 110, + 1, + 0, + 0, + 0, + 419, + 420, + 5, + 33, + 0, + 0, + 420, + 421, + 5, + 61, + 0, + 0, + 421, + 112, + 1, + 0, + 0, + 0, + 422, + 423, + 5, + 61, + 0, + 0, + 423, + 424, + 5, + 61, + 0, + 0, + 424, + 425, + 5, + 61, + 0, + 0, + 425, + 114, + 1, + 0, + 0, + 0, + 426, + 427, + 5, + 33, + 0, + 0, + 427, + 428, + 5, + 61, + 0, + 0, + 428, + 429, + 5, + 61, + 0, + 0, + 429, + 116, + 1, + 0, + 0, + 0, + 430, + 431, + 5, + 38, + 0, + 0, + 431, + 432, + 5, + 38, + 0, + 0, + 432, + 118, + 1, + 0, + 0, + 0, + 433, + 434, + 5, + 124, + 0, + 0, + 434, + 435, + 5, + 124, + 0, + 0, + 435, + 120, + 1, + 0, + 0, + 0, + 436, + 437, + 5, + 42, + 0, + 0, + 437, + 438, + 5, + 42, + 0, + 0, + 438, + 122, + 1, + 0, + 0, + 0, + 439, + 440, + 5, + 62, + 0, + 0, + 440, + 441, + 5, + 62, + 0, + 0, + 441, + 124, + 1, + 0, + 0, + 0, + 442, + 443, + 5, + 60, + 0, + 0, + 443, + 444, + 5, + 60, + 0, + 0, + 444, + 126, + 1, + 0, + 0, + 0, + 445, + 446, + 5, + 62, + 0, + 0, + 446, + 447, + 5, + 62, + 0, + 0, + 447, + 448, + 5, + 62, + 0, + 0, + 448, + 128, + 1, + 0, + 0, + 0, + 449, + 450, + 5, + 60, + 0, + 0, + 450, + 451, + 5, + 60, + 0, + 0, + 451, + 452, + 5, + 60, + 0, + 0, + 452, + 130, + 1, + 0, + 0, + 0, + 453, + 457, + 7, + 0, + 0, + 0, + 454, + 456, + 7, + 1, + 0, + 0, + 455, + 454, + 1, + 0, + 0, + 0, + 456, + 459, + 1, + 0, + 0, + 0, + 457, + 455, + 1, + 0, + 0, + 0, + 457, + 458, + 1, + 0, + 0, + 0, + 458, + 132, + 1, + 0, + 0, + 0, + 459, + 457, + 1, + 0, + 0, + 0, + 460, + 462, + 7, + 2, + 0, + 0, + 461, + 460, + 1, + 0, + 0, + 0, + 462, + 463, + 1, + 0, + 0, + 0, + 463, + 461, + 1, + 0, + 0, + 0, + 463, + 464, + 1, + 0, + 0, + 0, + 464, + 465, + 1, + 0, + 0, + 0, + 465, + 466, + 6, + 66, + 0, + 0, + 466, + 134, + 1, + 0, + 0, + 0, + 467, + 468, + 5, + 47, + 0, + 0, + 468, + 469, + 5, + 47, + 0, + 0, + 469, + 473, + 1, + 0, + 0, + 0, + 470, + 472, + 9, + 0, + 0, + 0, + 471, + 470, + 1, + 0, + 0, + 0, + 472, + 475, + 1, + 0, + 0, + 0, + 473, + 474, + 1, + 0, + 0, + 0, + 473, + 471, + 1, + 0, + 0, + 0, + 474, + 477, + 1, + 0, + 0, + 0, + 475, + 473, + 1, + 0, + 0, + 0, + 476, + 478, + 5, + 13, + 0, + 0, + 477, + 476, + 1, + 0, + 0, + 0, + 477, + 478, + 1, + 0, + 0, + 0, + 478, + 479, + 1, + 0, + 0, + 0, + 479, + 480, + 5, + 10, + 0, + 0, + 480, + 481, + 1, + 0, + 0, + 0, + 481, + 482, + 6, + 67, + 1, + 0, + 482, + 136, + 1, + 0, + 0, + 0, + 483, + 484, + 5, + 47, + 0, + 0, + 484, + 485, + 5, + 42, + 0, + 0, + 485, + 489, + 1, + 0, + 0, + 0, + 486, + 488, + 9, + 0, + 0, + 0, + 487, + 486, + 1, + 0, + 0, + 0, + 488, + 491, + 1, + 0, + 0, + 0, + 489, + 490, + 1, + 0, + 0, + 0, + 489, + 487, + 1, + 0, + 0, + 0, + 490, + 492, + 1, + 0, + 0, + 0, + 491, + 489, + 1, + 0, + 0, + 0, + 492, + 493, + 5, + 42, + 0, + 0, + 493, + 494, + 5, + 47, + 0, + 0, + 494, + 495, + 1, + 0, + 0, + 0, + 495, + 496, + 6, + 68, + 1, + 0, + 496, + 138, + 1, + 0, + 0, + 0, + 497, + 501, + 5, + 96, + 0, + 0, + 498, + 500, + 9, + 0, + 0, + 0, + 499, + 498, + 1, + 0, + 0, + 0, + 500, + 503, + 1, + 0, + 0, + 0, + 501, + 502, + 1, + 0, + 0, + 0, + 501, + 499, + 1, + 0, + 0, + 0, + 502, + 505, + 1, + 0, + 0, + 0, + 503, + 501, + 1, + 0, + 0, + 0, + 504, + 506, + 5, + 13, + 0, + 0, + 505, + 504, + 1, + 0, + 0, + 0, + 505, + 506, + 1, + 0, + 0, + 0, + 506, + 507, + 1, + 0, + 0, + 0, + 507, + 508, + 5, + 10, + 0, + 0, + 508, + 509, + 1, + 0, + 0, + 0, + 509, + 510, + 6, + 69, + 1, + 0, + 510, + 140, + 1, + 0, + 0, + 0, + 511, + 512, + 3, + 157, + 78, + 0, + 512, + 513, + 5, + 46, + 0, + 0, + 513, + 514, + 3, + 157, + 78, + 0, + 514, + 527, + 1, + 0, + 0, + 0, + 515, + 518, + 3, + 157, + 78, + 0, + 516, + 517, + 5, + 46, + 0, + 0, + 517, + 519, + 3, + 157, + 78, + 0, + 518, + 516, + 1, + 0, + 0, + 0, + 518, + 519, + 1, + 0, + 0, + 0, + 519, + 520, + 1, + 0, + 0, + 0, + 520, + 522, + 7, + 3, + 0, + 0, + 521, + 523, + 7, + 4, + 0, + 0, + 522, + 521, + 1, + 0, + 0, + 0, + 522, + 523, + 1, + 0, + 0, + 0, + 523, + 524, + 1, + 0, + 0, + 0, + 524, + 525, + 3, + 157, + 78, + 0, + 525, + 527, + 1, + 0, + 0, + 0, + 526, + 511, + 1, + 0, + 0, + 0, + 526, + 515, + 1, + 0, + 0, + 0, + 527, + 142, + 1, + 0, + 0, + 0, + 528, + 558, + 3, + 157, + 78, + 0, + 529, + 531, + 3, + 153, + 76, + 0, + 530, + 529, + 1, + 0, + 0, + 0, + 530, + 531, + 1, + 0, + 0, + 0, + 531, + 532, + 1, + 0, + 0, + 0, + 532, + 533, + 3, + 165, + 82, + 0, + 533, + 534, + 3, + 157, + 78, + 0, + 534, + 558, + 1, + 0, + 0, + 0, + 535, + 537, + 3, + 153, + 76, + 0, + 536, + 535, + 1, + 0, + 0, + 0, + 536, + 537, + 1, + 0, + 0, + 0, + 537, + 538, + 1, + 0, + 0, + 0, + 538, + 539, + 3, + 165, + 82, + 0, + 539, + 543, + 3, + 183, + 91, + 0, + 540, + 542, + 5, + 95, + 0, + 0, + 541, + 540, + 1, + 0, + 0, + 0, + 542, + 545, + 1, + 0, + 0, + 0, + 543, + 541, + 1, + 0, + 0, + 0, + 543, + 544, + 1, + 0, + 0, + 0, + 544, + 558, + 1, + 0, + 0, + 0, + 545, + 543, + 1, + 0, + 0, + 0, + 546, + 548, + 3, + 153, + 76, + 0, + 547, + 546, + 1, + 0, + 0, + 0, + 547, + 548, + 1, + 0, + 0, + 0, + 548, + 549, + 1, + 0, + 0, + 0, + 549, + 550, + 3, + 165, + 82, + 0, + 550, + 554, + 3, + 185, + 92, + 0, + 551, + 553, + 5, + 95, + 0, + 0, + 552, + 551, + 1, + 0, + 0, + 0, + 553, + 556, + 1, + 0, + 0, + 0, + 554, + 552, + 1, + 0, + 0, + 0, + 554, + 555, + 1, + 0, + 0, + 0, + 555, + 558, + 1, + 0, + 0, + 0, + 556, + 554, + 1, + 0, + 0, + 0, + 557, + 528, + 1, + 0, + 0, + 0, + 557, + 530, + 1, + 0, + 0, + 0, + 557, + 536, + 1, + 0, + 0, + 0, + 557, + 547, + 1, + 0, + 0, + 0, + 558, + 144, + 1, + 0, + 0, + 0, + 559, + 561, + 3, + 153, + 76, + 0, + 560, + 559, + 1, + 0, + 0, + 0, + 560, + 561, + 1, + 0, + 0, + 0, + 561, + 562, + 1, + 0, + 0, + 0, + 562, + 563, + 3, + 167, + 83, + 0, + 563, + 564, + 3, + 159, + 79, + 0, + 564, + 146, + 1, + 0, + 0, + 0, + 565, + 567, + 3, + 153, + 76, + 0, + 566, + 565, + 1, + 0, + 0, + 0, + 566, + 567, + 1, + 0, + 0, + 0, + 567, + 568, + 1, + 0, + 0, + 0, + 568, + 569, + 3, + 169, + 84, + 0, + 569, + 570, + 3, + 161, + 80, + 0, + 570, + 148, + 1, + 0, + 0, + 0, + 571, + 573, + 3, + 153, + 76, + 0, + 572, + 571, + 1, + 0, + 0, + 0, + 572, + 573, + 1, + 0, + 0, + 0, + 573, + 574, + 1, + 0, + 0, + 0, + 574, + 575, + 3, + 171, + 85, + 0, + 575, + 576, + 3, + 163, + 81, + 0, + 576, + 150, + 1, + 0, + 0, + 0, + 577, + 578, + 7, + 4, + 0, + 0, + 578, + 152, + 1, + 0, + 0, + 0, + 579, + 580, + 3, + 155, + 77, + 0, + 580, + 154, + 1, + 0, + 0, + 0, + 581, + 586, + 3, + 173, + 86, + 0, + 582, + 585, + 5, + 95, + 0, + 0, + 583, + 585, + 3, + 175, + 87, + 0, + 584, + 582, + 1, + 0, + 0, + 0, + 584, + 583, + 1, + 0, + 0, + 0, + 585, + 588, + 1, + 0, + 0, + 0, + 586, + 584, + 1, + 0, + 0, + 0, + 586, + 587, + 1, + 0, + 0, + 0, + 587, + 156, + 1, + 0, + 0, + 0, + 588, + 586, + 1, + 0, + 0, + 0, + 589, + 594, + 3, + 175, + 87, + 0, + 590, + 593, + 5, + 95, + 0, + 0, + 591, + 593, + 3, + 175, + 87, + 0, + 592, + 590, + 1, + 0, + 0, + 0, + 592, + 591, + 1, + 0, + 0, + 0, + 593, + 596, + 1, + 0, + 0, + 0, + 594, + 592, + 1, + 0, + 0, + 0, + 594, + 595, + 1, + 0, + 0, + 0, + 595, + 158, + 1, + 0, + 0, + 0, + 596, + 594, + 1, + 0, + 0, + 0, + 597, + 602, + 3, + 177, + 88, + 0, + 598, + 601, + 5, + 95, + 0, + 0, + 599, + 601, + 3, + 177, + 88, + 0, + 600, + 598, + 1, + 0, + 0, + 0, + 600, + 599, + 1, + 0, + 0, + 0, + 601, + 604, + 1, + 0, + 0, + 0, + 602, + 600, + 1, + 0, + 0, + 0, + 602, + 603, + 1, + 0, + 0, + 0, + 603, + 160, + 1, + 0, + 0, + 0, + 604, + 602, + 1, + 0, + 0, + 0, + 605, + 610, + 3, + 179, + 89, + 0, + 606, + 609, + 5, + 95, + 0, + 0, + 607, + 609, + 3, + 179, + 89, + 0, + 608, + 606, + 1, + 0, + 0, + 0, + 608, + 607, + 1, + 0, + 0, + 0, + 609, + 612, + 1, + 0, + 0, + 0, + 610, + 608, + 1, + 0, + 0, + 0, + 610, + 611, + 1, + 0, + 0, + 0, + 611, + 162, + 1, + 0, + 0, + 0, + 612, + 610, + 1, + 0, + 0, + 0, + 613, + 618, + 3, + 181, + 90, + 0, + 614, + 617, + 5, + 95, + 0, + 0, + 615, + 617, + 3, + 181, + 90, + 0, + 616, + 614, + 1, + 0, + 0, + 0, + 616, + 615, + 1, + 0, + 0, + 0, + 617, + 620, + 1, + 0, + 0, + 0, + 618, + 616, + 1, + 0, + 0, + 0, + 618, + 619, + 1, + 0, + 0, + 0, + 619, + 164, + 1, + 0, + 0, + 0, + 620, + 618, + 1, + 0, + 0, + 0, + 621, + 623, + 5, + 39, + 0, + 0, + 622, + 624, + 7, + 5, + 0, + 0, + 623, + 622, + 1, + 0, + 0, + 0, + 623, + 624, + 1, + 0, + 0, + 0, + 624, + 625, + 1, + 0, + 0, + 0, + 625, + 626, + 7, + 6, + 0, + 0, + 626, + 166, + 1, + 0, + 0, + 0, + 627, + 629, + 5, + 39, + 0, + 0, + 628, + 630, + 7, + 5, + 0, + 0, + 629, + 628, + 1, + 0, + 0, + 0, + 629, + 630, + 1, + 0, + 0, + 0, + 630, + 631, + 1, + 0, + 0, + 0, + 631, + 632, + 7, + 7, + 0, + 0, + 632, + 168, + 1, + 0, + 0, + 0, + 633, + 635, + 5, + 39, + 0, + 0, + 634, + 636, + 7, + 5, + 0, + 0, + 635, + 634, + 1, + 0, + 0, + 0, + 635, + 636, + 1, + 0, + 0, + 0, + 636, + 637, + 1, + 0, + 0, + 0, + 637, + 638, + 7, + 8, + 0, + 0, + 638, + 170, + 1, + 0, + 0, + 0, + 639, + 641, + 5, + 39, + 0, + 0, + 640, + 642, + 7, + 5, + 0, + 0, + 641, + 640, + 1, + 0, + 0, + 0, + 641, + 642, + 1, + 0, + 0, + 0, + 642, + 643, + 1, + 0, + 0, + 0, + 643, + 644, + 7, + 9, + 0, + 0, + 644, + 172, + 1, + 0, + 0, + 0, + 645, + 646, + 7, + 10, + 0, + 0, + 646, + 174, + 1, + 0, + 0, + 0, + 647, + 648, + 7, + 11, + 0, + 0, + 648, + 176, + 1, + 0, + 0, + 0, + 649, + 653, + 3, + 183, + 91, + 0, + 650, + 653, + 3, + 185, + 92, + 0, + 651, + 653, + 7, + 12, + 0, + 0, + 652, + 649, + 1, + 0, + 0, + 0, + 652, + 650, + 1, + 0, + 0, + 0, + 652, + 651, + 1, + 0, + 0, + 0, + 653, + 178, + 1, + 0, + 0, + 0, + 654, + 658, + 3, + 183, + 91, + 0, + 655, + 658, + 3, + 185, + 92, + 0, + 656, + 658, + 7, + 13, + 0, + 0, + 657, + 654, + 1, + 0, + 0, + 0, + 657, + 655, + 1, + 0, + 0, + 0, + 657, + 656, + 1, + 0, + 0, + 0, + 658, + 180, + 1, + 0, + 0, + 0, + 659, + 663, + 3, + 183, + 91, + 0, + 660, + 663, + 3, + 185, + 92, + 0, + 661, + 663, + 7, + 14, + 0, + 0, + 662, + 659, + 1, + 0, + 0, + 0, + 662, + 660, + 1, + 0, + 0, + 0, + 662, + 661, + 1, + 0, + 0, + 0, + 663, + 182, + 1, + 0, + 0, + 0, + 664, + 665, + 7, + 15, + 0, + 0, + 665, + 184, + 1, + 0, + 0, + 0, + 666, + 667, + 7, + 16, + 0, + 0, + 667, + 186, + 1, + 0, + 0, + 0, + 37, + 0, + 457, + 463, + 473, + 477, + 489, + 501, + 505, + 518, + 522, + 526, + 530, + 536, + 543, + 547, + 554, + 557, + 560, + 566, + 572, + 584, + 586, + 592, + 594, + 600, + 602, + 608, + 610, + 616, + 618, + 623, + 629, + 635, + 641, + 652, + 657, + 662, + 2, + 6, + 0, + 0, + 0, + 1, + 0, + ] class lfrXLexer(Lexer): - atn = ATNDeserializer().deserialize(serializedATN()) - decisionsToDFA = [ DFA(ds, i) for i, ds in enumerate(atn.decisionToState) ] + decisionsToDFA = [DFA(ds, i) for i, ds in enumerate(atn.decisionToState)] T__0 = 1 T__1 = 2 @@ -392,53 +5966,196 @@ class lfrXLexer(Lexer): Octal_number = 74 Hex_number = 75 - channelNames = [ u"DEFAULT_TOKEN_CHANNEL", u"HIDDEN" ] + channelNames = ["DEFAULT_TOKEN_CHANNEL", "HIDDEN"] - modeNames = [ "DEFAULT_MODE" ] + modeNames = ["DEFAULT_MODE"] - literalNames = [ "", - "'endmodule'", "'module'", "'('", "')'", "';'", "','", "'finput'", - "'foutput'", "'control'", "'distribute@'", "'begin'", "'end'", - "'if'", "'else'", "'endcase'", "'case'", "':'", "'default'", - "'<='", "'.'", "'signal'", "'flow'", "'storage'", "'pump'", - "'number'", "'assign'", "'='", "'['", "']'", "'{'", "'}'", "'#MAP'", - "'\"'", "'#MATERIAL'", "'#CONSTRAIN'", "'AND'", "'OR'", "'>'", - "'<'", "'>='", "'+'", "'-'", "'!'", "'~'", "'&'", "'~&'", "'|'", - "'~|'", "'^'", "'~^'", "'^~'", "'*'", "'/'", "'%'", "'=='", - "'!='", "'==='", "'!=='", "'&&'", "'||'", "'**'", "'>>'", "'<<'", - "'>>>'", "'<<<'" ] + literalNames = [ + "", + "'endmodule'", + "'module'", + "'('", + "')'", + "';'", + "','", + "'finput'", + "'foutput'", + "'control'", + "'distribute@'", + "'begin'", + "'end'", + "'if'", + "'else'", + "'endcase'", + "'case'", + "':'", + "'default'", + "'<='", + "'.'", + "'signal'", + "'flow'", + "'storage'", + "'pump'", + "'number'", + "'assign'", + "'='", + "'['", + "']'", + "'{'", + "'}'", + "'#MAP'", + "'\"'", + "'#MATERIAL'", + "'#CONSTRAIN'", + "'AND'", + "'OR'", + "'>'", + "'<'", + "'>='", + "'+'", + "'-'", + "'!'", + "'~'", + "'&'", + "'~&'", + "'|'", + "'~|'", + "'^'", + "'~^'", + "'^~'", + "'*'", + "'/'", + "'%'", + "'=='", + "'!='", + "'==='", + "'!=='", + "'&&'", + "'||'", + "'**'", + "'>>'", + "'<<'", + "'>>>'", + "'<<<'", + ] - symbolicNames = [ "", - "ID", "WS", "One_line_comment", "Block_comment", "Import_line", - "Real_number", "Decimal_number", "Binary_number", "Octal_number", - "Hex_number" ] + symbolicNames = [ + "", + "ID", + "WS", + "One_line_comment", + "Block_comment", + "Import_line", + "Real_number", + "Decimal_number", + "Binary_number", + "Octal_number", + "Hex_number", + ] - ruleNames = [ "T__0", "T__1", "T__2", "T__3", "T__4", "T__5", "T__6", - "T__7", "T__8", "T__9", "T__10", "T__11", "T__12", "T__13", - "T__14", "T__15", "T__16", "T__17", "T__18", "T__19", - "T__20", "T__21", "T__22", "T__23", "T__24", "T__25", - "T__26", "T__27", "T__28", "T__29", "T__30", "T__31", - "T__32", "T__33", "T__34", "T__35", "T__36", "T__37", - "T__38", "T__39", "T__40", "T__41", "T__42", "T__43", - "T__44", "T__45", "T__46", "T__47", "T__48", "T__49", - "T__50", "T__51", "T__52", "T__53", "T__54", "T__55", - "T__56", "T__57", "T__58", "T__59", "T__60", "T__61", - "T__62", "T__63", "T__64", "ID", "WS", "One_line_comment", - "Block_comment", "Import_line", "Real_number", "Decimal_number", - "Binary_number", "Octal_number", "Hex_number", "Sign", - "Size", "Non_zero_unsigned_number", "Unsigned_number", - "Binary_value", "Octal_value", "Hex_value", "Decimal_base", - "Binary_base", "Octal_base", "Hex_base", "Non_zero_decimal_digit", - "Decimal_digit", "Binary_digit", "Octal_digit", "Hex_digit", - "X_digit", "Z_digit" ] + ruleNames = [ + "T__0", + "T__1", + "T__2", + "T__3", + "T__4", + "T__5", + "T__6", + "T__7", + "T__8", + "T__9", + "T__10", + "T__11", + "T__12", + "T__13", + "T__14", + "T__15", + "T__16", + "T__17", + "T__18", + "T__19", + "T__20", + "T__21", + "T__22", + "T__23", + "T__24", + "T__25", + "T__26", + "T__27", + "T__28", + "T__29", + "T__30", + "T__31", + "T__32", + "T__33", + "T__34", + "T__35", + "T__36", + "T__37", + "T__38", + "T__39", + "T__40", + "T__41", + "T__42", + "T__43", + "T__44", + "T__45", + "T__46", + "T__47", + "T__48", + "T__49", + "T__50", + "T__51", + "T__52", + "T__53", + "T__54", + "T__55", + "T__56", + "T__57", + "T__58", + "T__59", + "T__60", + "T__61", + "T__62", + "T__63", + "T__64", + "ID", + "WS", + "One_line_comment", + "Block_comment", + "Import_line", + "Real_number", + "Decimal_number", + "Binary_number", + "Octal_number", + "Hex_number", + "Sign", + "Size", + "Non_zero_unsigned_number", + "Unsigned_number", + "Binary_value", + "Octal_value", + "Hex_value", + "Decimal_base", + "Binary_base", + "Octal_base", + "Hex_base", + "Non_zero_decimal_digit", + "Decimal_digit", + "Binary_digit", + "Octal_digit", + "Hex_digit", + "X_digit", + "Z_digit", + ] grammarFileName = "lfrX.g4" - def __init__(self, input=None, output:TextIO = sys.stdout): + def __init__(self, input=None, output: TextIO = sys.stdout): super().__init__(input, output) - self.checkVersion("4.9.1") - self._interp = LexerATNSimulator(self, self.atn, self.decisionsToDFA, PredictionContextCache()) + self.checkVersion("4.10.1") + self._interp = LexerATNSimulator( + self, self.atn, self.decisionsToDFA, PredictionContextCache() + ) self._actions = None self._predicates = None - - diff --git a/lfr/antlrgen/lfr/lfrXListener.py b/lfr/antlrgen/lfr/lfrXListener.py index 9297a88..f7ea614 100755 --- a/lfr/antlrgen/lfr/lfrXListener.py +++ b/lfr/antlrgen/lfr/lfrXListener.py @@ -1,597 +1,561 @@ -# Generated from ./lfrX.g4 by ANTLR 4.9.1 +# Generated from ./lfrX.g4 by ANTLR 4.10.1 from antlr4 import * + if __name__ is not None and "." in __name__: from .lfrXParser import lfrXParser else: from lfrXParser import lfrXParser + # This class defines a complete listener for a parse tree produced by lfrXParser. class lfrXListener(ParseTreeListener): - # Enter a parse tree produced by lfrXParser#skeleton. - def enterSkeleton(self, ctx:lfrXParser.SkeletonContext): + def enterSkeleton(self, ctx: lfrXParser.SkeletonContext): pass # Exit a parse tree produced by lfrXParser#skeleton. - def exitSkeleton(self, ctx:lfrXParser.SkeletonContext): + def exitSkeleton(self, ctx: lfrXParser.SkeletonContext): pass - # Enter a parse tree produced by lfrXParser#module. - def enterModule(self, ctx:lfrXParser.ModuleContext): + def enterModule(self, ctx: lfrXParser.ModuleContext): pass # Exit a parse tree produced by lfrXParser#module. - def exitModule(self, ctx:lfrXParser.ModuleContext): + def exitModule(self, ctx: lfrXParser.ModuleContext): pass - # Enter a parse tree produced by lfrXParser#moduledefinition. - def enterModuledefinition(self, ctx:lfrXParser.ModuledefinitionContext): + def enterModuledefinition(self, ctx: lfrXParser.ModuledefinitionContext): pass # Exit a parse tree produced by lfrXParser#moduledefinition. - def exitModuledefinition(self, ctx:lfrXParser.ModuledefinitionContext): + def exitModuledefinition(self, ctx: lfrXParser.ModuledefinitionContext): pass - # Enter a parse tree produced by lfrXParser#body. - def enterBody(self, ctx:lfrXParser.BodyContext): + def enterBody(self, ctx: lfrXParser.BodyContext): pass # Exit a parse tree produced by lfrXParser#body. - def exitBody(self, ctx:lfrXParser.BodyContext): + def exitBody(self, ctx: lfrXParser.BodyContext): pass - # Enter a parse tree produced by lfrXParser#ioblock. - def enterIoblock(self, ctx:lfrXParser.IoblockContext): + def enterIoblock(self, ctx: lfrXParser.IoblockContext): pass # Exit a parse tree produced by lfrXParser#ioblock. - def exitIoblock(self, ctx:lfrXParser.IoblockContext): + def exitIoblock(self, ctx: lfrXParser.IoblockContext): pass - # Enter a parse tree produced by lfrXParser#vectorvar. - def enterVectorvar(self, ctx:lfrXParser.VectorvarContext): + def enterVectorvar(self, ctx: lfrXParser.VectorvarContext): pass # Exit a parse tree produced by lfrXParser#vectorvar. - def exitVectorvar(self, ctx:lfrXParser.VectorvarContext): + def exitVectorvar(self, ctx: lfrXParser.VectorvarContext): pass - # Enter a parse tree produced by lfrXParser#explicitIOBlock. - def enterExplicitIOBlock(self, ctx:lfrXParser.ExplicitIOBlockContext): + def enterExplicitIOBlock(self, ctx: lfrXParser.ExplicitIOBlockContext): pass # Exit a parse tree produced by lfrXParser#explicitIOBlock. - def exitExplicitIOBlock(self, ctx:lfrXParser.ExplicitIOBlockContext): + def exitExplicitIOBlock(self, ctx: lfrXParser.ExplicitIOBlockContext): pass - # Enter a parse tree produced by lfrXParser#declvar. - def enterDeclvar(self, ctx:lfrXParser.DeclvarContext): + def enterDeclvar(self, ctx: lfrXParser.DeclvarContext): pass # Exit a parse tree produced by lfrXParser#declvar. - def exitDeclvar(self, ctx:lfrXParser.DeclvarContext): + def exitDeclvar(self, ctx: lfrXParser.DeclvarContext): pass - # Enter a parse tree produced by lfrXParser#distributionBlock. - def enterDistributionBlock(self, ctx:lfrXParser.DistributionBlockContext): + def enterDistributionBlock(self, ctx: lfrXParser.DistributionBlockContext): pass # Exit a parse tree produced by lfrXParser#distributionBlock. - def exitDistributionBlock(self, ctx:lfrXParser.DistributionBlockContext): + def exitDistributionBlock(self, ctx: lfrXParser.DistributionBlockContext): pass - # Enter a parse tree produced by lfrXParser#distributionBody. - def enterDistributionBody(self, ctx:lfrXParser.DistributionBodyContext): + def enterDistributionBody(self, ctx: lfrXParser.DistributionBodyContext): pass # Exit a parse tree produced by lfrXParser#distributionBody. - def exitDistributionBody(self, ctx:lfrXParser.DistributionBodyContext): + def exitDistributionBody(self, ctx: lfrXParser.DistributionBodyContext): pass - # Enter a parse tree produced by lfrXParser#distributeBodyStat. - def enterDistributeBodyStat(self, ctx:lfrXParser.DistributeBodyStatContext): + def enterDistributeBodyStat(self, ctx: lfrXParser.DistributeBodyStatContext): pass # Exit a parse tree produced by lfrXParser#distributeBodyStat. - def exitDistributeBodyStat(self, ctx:lfrXParser.DistributeBodyStatContext): + def exitDistributeBodyStat(self, ctx: lfrXParser.DistributeBodyStatContext): pass - # Enter a parse tree produced by lfrXParser#ifElseBlock. - def enterIfElseBlock(self, ctx:lfrXParser.IfElseBlockContext): + def enterIfElseBlock(self, ctx: lfrXParser.IfElseBlockContext): pass # Exit a parse tree produced by lfrXParser#ifElseBlock. - def exitIfElseBlock(self, ctx:lfrXParser.IfElseBlockContext): + def exitIfElseBlock(self, ctx: lfrXParser.IfElseBlockContext): pass - # Enter a parse tree produced by lfrXParser#ifBlock. - def enterIfBlock(self, ctx:lfrXParser.IfBlockContext): + def enterIfBlock(self, ctx: lfrXParser.IfBlockContext): pass # Exit a parse tree produced by lfrXParser#ifBlock. - def exitIfBlock(self, ctx:lfrXParser.IfBlockContext): + def exitIfBlock(self, ctx: lfrXParser.IfBlockContext): pass - # Enter a parse tree produced by lfrXParser#elseBlock. - def enterElseBlock(self, ctx:lfrXParser.ElseBlockContext): + def enterElseBlock(self, ctx: lfrXParser.ElseBlockContext): pass # Exit a parse tree produced by lfrXParser#elseBlock. - def exitElseBlock(self, ctx:lfrXParser.ElseBlockContext): + def exitElseBlock(self, ctx: lfrXParser.ElseBlockContext): pass - # Enter a parse tree produced by lfrXParser#elseIfBlock. - def enterElseIfBlock(self, ctx:lfrXParser.ElseIfBlockContext): + def enterElseIfBlock(self, ctx: lfrXParser.ElseIfBlockContext): pass # Exit a parse tree produced by lfrXParser#elseIfBlock. - def exitElseIfBlock(self, ctx:lfrXParser.ElseIfBlockContext): + def exitElseIfBlock(self, ctx: lfrXParser.ElseIfBlockContext): pass - # Enter a parse tree produced by lfrXParser#distributeCondition. - def enterDistributeCondition(self, ctx:lfrXParser.DistributeConditionContext): + def enterDistributeCondition(self, ctx: lfrXParser.DistributeConditionContext): pass # Exit a parse tree produced by lfrXParser#distributeCondition. - def exitDistributeCondition(self, ctx:lfrXParser.DistributeConditionContext): + def exitDistributeCondition(self, ctx: lfrXParser.DistributeConditionContext): pass - # Enter a parse tree produced by lfrXParser#statementBlock. - def enterStatementBlock(self, ctx:lfrXParser.StatementBlockContext): + def enterStatementBlock(self, ctx: lfrXParser.StatementBlockContext): pass # Exit a parse tree produced by lfrXParser#statementBlock. - def exitStatementBlock(self, ctx:lfrXParser.StatementBlockContext): + def exitStatementBlock(self, ctx: lfrXParser.StatementBlockContext): pass - # Enter a parse tree produced by lfrXParser#caseBlock. - def enterCaseBlock(self, ctx:lfrXParser.CaseBlockContext): + def enterCaseBlock(self, ctx: lfrXParser.CaseBlockContext): pass # Exit a parse tree produced by lfrXParser#caseBlock. - def exitCaseBlock(self, ctx:lfrXParser.CaseBlockContext): + def exitCaseBlock(self, ctx: lfrXParser.CaseBlockContext): pass - # Enter a parse tree produced by lfrXParser#caseBlockHeader. - def enterCaseBlockHeader(self, ctx:lfrXParser.CaseBlockHeaderContext): + def enterCaseBlockHeader(self, ctx: lfrXParser.CaseBlockHeaderContext): pass # Exit a parse tree produced by lfrXParser#caseBlockHeader. - def exitCaseBlockHeader(self, ctx:lfrXParser.CaseBlockHeaderContext): + def exitCaseBlockHeader(self, ctx: lfrXParser.CaseBlockHeaderContext): pass - # Enter a parse tree produced by lfrXParser#casestat. - def enterCasestat(self, ctx:lfrXParser.CasestatContext): + def enterCasestat(self, ctx: lfrXParser.CasestatContext): pass # Exit a parse tree produced by lfrXParser#casestat. - def exitCasestat(self, ctx:lfrXParser.CasestatContext): + def exitCasestat(self, ctx: lfrXParser.CasestatContext): pass - # Enter a parse tree produced by lfrXParser#defaultCaseStat. - def enterDefaultCaseStat(self, ctx:lfrXParser.DefaultCaseStatContext): + def enterDefaultCaseStat(self, ctx: lfrXParser.DefaultCaseStatContext): pass # Exit a parse tree produced by lfrXParser#defaultCaseStat. - def exitDefaultCaseStat(self, ctx:lfrXParser.DefaultCaseStatContext): + def exitDefaultCaseStat(self, ctx: lfrXParser.DefaultCaseStatContext): pass - # Enter a parse tree produced by lfrXParser#distvalue. - def enterDistvalue(self, ctx:lfrXParser.DistvalueContext): + def enterDistvalue(self, ctx: lfrXParser.DistvalueContext): pass # Exit a parse tree produced by lfrXParser#distvalue. - def exitDistvalue(self, ctx:lfrXParser.DistvalueContext): + def exitDistvalue(self, ctx: lfrXParser.DistvalueContext): pass - # Enter a parse tree produced by lfrXParser#distributionassignstat. - def enterDistributionassignstat(self, ctx:lfrXParser.DistributionassignstatContext): + def enterDistributionassignstat( + self, ctx: lfrXParser.DistributionassignstatContext + ): pass # Exit a parse tree produced by lfrXParser#distributionassignstat. - def exitDistributionassignstat(self, ctx:lfrXParser.DistributionassignstatContext): + def exitDistributionassignstat(self, ctx: lfrXParser.DistributionassignstatContext): pass - # Enter a parse tree produced by lfrXParser#sensitivitylist. - def enterSensitivitylist(self, ctx:lfrXParser.SensitivitylistContext): + def enterSensitivitylist(self, ctx: lfrXParser.SensitivitylistContext): pass # Exit a parse tree produced by lfrXParser#sensitivitylist. - def exitSensitivitylist(self, ctx:lfrXParser.SensitivitylistContext): + def exitSensitivitylist(self, ctx: lfrXParser.SensitivitylistContext): pass - # Enter a parse tree produced by lfrXParser#signal. - def enterSignal(self, ctx:lfrXParser.SignalContext): + def enterSignal(self, ctx: lfrXParser.SignalContext): pass # Exit a parse tree produced by lfrXParser#signal. - def exitSignal(self, ctx:lfrXParser.SignalContext): + def exitSignal(self, ctx: lfrXParser.SignalContext): pass - # Enter a parse tree produced by lfrXParser#statements. - def enterStatements(self, ctx:lfrXParser.StatementsContext): + def enterStatements(self, ctx: lfrXParser.StatementsContext): pass # Exit a parse tree produced by lfrXParser#statements. - def exitStatements(self, ctx:lfrXParser.StatementsContext): + def exitStatements(self, ctx: lfrXParser.StatementsContext): pass - # Enter a parse tree produced by lfrXParser#statement. - def enterStatement(self, ctx:lfrXParser.StatementContext): + def enterStatement(self, ctx: lfrXParser.StatementContext): pass # Exit a parse tree produced by lfrXParser#statement. - def exitStatement(self, ctx:lfrXParser.StatementContext): + def exitStatement(self, ctx: lfrXParser.StatementContext): pass - # Enter a parse tree produced by lfrXParser#moduleinstantiationstat. - def enterModuleinstantiationstat(self, ctx:lfrXParser.ModuleinstantiationstatContext): + def enterModuleinstantiationstat( + self, ctx: lfrXParser.ModuleinstantiationstatContext + ): pass # Exit a parse tree produced by lfrXParser#moduleinstantiationstat. - def exitModuleinstantiationstat(self, ctx:lfrXParser.ModuleinstantiationstatContext): + def exitModuleinstantiationstat( + self, ctx: lfrXParser.ModuleinstantiationstatContext + ): pass - # Enter a parse tree produced by lfrXParser#instanceioblock. - def enterInstanceioblock(self, ctx:lfrXParser.InstanceioblockContext): + def enterInstanceioblock(self, ctx: lfrXParser.InstanceioblockContext): pass # Exit a parse tree produced by lfrXParser#instanceioblock. - def exitInstanceioblock(self, ctx:lfrXParser.InstanceioblockContext): + def exitInstanceioblock(self, ctx: lfrXParser.InstanceioblockContext): pass - # Enter a parse tree produced by lfrXParser#orderedioblock. - def enterOrderedioblock(self, ctx:lfrXParser.OrderedioblockContext): + def enterOrderedioblock(self, ctx: lfrXParser.OrderedioblockContext): pass # Exit a parse tree produced by lfrXParser#orderedioblock. - def exitOrderedioblock(self, ctx:lfrXParser.OrderedioblockContext): + def exitOrderedioblock(self, ctx: lfrXParser.OrderedioblockContext): pass - # Enter a parse tree produced by lfrXParser#unorderedioblock. - def enterUnorderedioblock(self, ctx:lfrXParser.UnorderedioblockContext): + def enterUnorderedioblock(self, ctx: lfrXParser.UnorderedioblockContext): pass # Exit a parse tree produced by lfrXParser#unorderedioblock. - def exitUnorderedioblock(self, ctx:lfrXParser.UnorderedioblockContext): + def exitUnorderedioblock(self, ctx: lfrXParser.UnorderedioblockContext): pass - # Enter a parse tree produced by lfrXParser#explicitinstanceiomapping. - def enterExplicitinstanceiomapping(self, ctx:lfrXParser.ExplicitinstanceiomappingContext): + def enterExplicitinstanceiomapping( + self, ctx: lfrXParser.ExplicitinstanceiomappingContext + ): pass # Exit a parse tree produced by lfrXParser#explicitinstanceiomapping. - def exitExplicitinstanceiomapping(self, ctx:lfrXParser.ExplicitinstanceiomappingContext): + def exitExplicitinstanceiomapping( + self, ctx: lfrXParser.ExplicitinstanceiomappingContext + ): pass - # Enter a parse tree produced by lfrXParser#instancename. - def enterInstancename(self, ctx:lfrXParser.InstancenameContext): + def enterInstancename(self, ctx: lfrXParser.InstancenameContext): pass # Exit a parse tree produced by lfrXParser#instancename. - def exitInstancename(self, ctx:lfrXParser.InstancenameContext): + def exitInstancename(self, ctx: lfrXParser.InstancenameContext): pass - # Enter a parse tree produced by lfrXParser#moduletype. - def enterModuletype(self, ctx:lfrXParser.ModuletypeContext): + def enterModuletype(self, ctx: lfrXParser.ModuletypeContext): pass # Exit a parse tree produced by lfrXParser#moduletype. - def exitModuletype(self, ctx:lfrXParser.ModuletypeContext): + def exitModuletype(self, ctx: lfrXParser.ModuletypeContext): pass - # Enter a parse tree produced by lfrXParser#tempvariablesstat. - def enterTempvariablesstat(self, ctx:lfrXParser.TempvariablesstatContext): + def enterTempvariablesstat(self, ctx: lfrXParser.TempvariablesstatContext): pass # Exit a parse tree produced by lfrXParser#tempvariablesstat. - def exitTempvariablesstat(self, ctx:lfrXParser.TempvariablesstatContext): + def exitTempvariablesstat(self, ctx: lfrXParser.TempvariablesstatContext): pass - # Enter a parse tree produced by lfrXParser#signalvarstat. - def enterSignalvarstat(self, ctx:lfrXParser.SignalvarstatContext): + def enterSignalvarstat(self, ctx: lfrXParser.SignalvarstatContext): pass # Exit a parse tree produced by lfrXParser#signalvarstat. - def exitSignalvarstat(self, ctx:lfrXParser.SignalvarstatContext): + def exitSignalvarstat(self, ctx: lfrXParser.SignalvarstatContext): pass - # Enter a parse tree produced by lfrXParser#fluiddeclstat. - def enterFluiddeclstat(self, ctx:lfrXParser.FluiddeclstatContext): + def enterFluiddeclstat(self, ctx: lfrXParser.FluiddeclstatContext): pass # Exit a parse tree produced by lfrXParser#fluiddeclstat. - def exitFluiddeclstat(self, ctx:lfrXParser.FluiddeclstatContext): + def exitFluiddeclstat(self, ctx: lfrXParser.FluiddeclstatContext): pass - # Enter a parse tree produced by lfrXParser#storagestat. - def enterStoragestat(self, ctx:lfrXParser.StoragestatContext): + def enterStoragestat(self, ctx: lfrXParser.StoragestatContext): pass # Exit a parse tree produced by lfrXParser#storagestat. - def exitStoragestat(self, ctx:lfrXParser.StoragestatContext): + def exitStoragestat(self, ctx: lfrXParser.StoragestatContext): pass - # Enter a parse tree produced by lfrXParser#pumpvarstat. - def enterPumpvarstat(self, ctx:lfrXParser.PumpvarstatContext): + def enterPumpvarstat(self, ctx: lfrXParser.PumpvarstatContext): pass # Exit a parse tree produced by lfrXParser#pumpvarstat. - def exitPumpvarstat(self, ctx:lfrXParser.PumpvarstatContext): + def exitPumpvarstat(self, ctx: lfrXParser.PumpvarstatContext): pass - # Enter a parse tree produced by lfrXParser#numvarstat. - def enterNumvarstat(self, ctx:lfrXParser.NumvarstatContext): + def enterNumvarstat(self, ctx: lfrXParser.NumvarstatContext): pass # Exit a parse tree produced by lfrXParser#numvarstat. - def exitNumvarstat(self, ctx:lfrXParser.NumvarstatContext): + def exitNumvarstat(self, ctx: lfrXParser.NumvarstatContext): pass - # Enter a parse tree produced by lfrXParser#assignstat. - def enterAssignstat(self, ctx:lfrXParser.AssignstatContext): + def enterAssignstat(self, ctx: lfrXParser.AssignstatContext): pass # Exit a parse tree produced by lfrXParser#assignstat. - def exitAssignstat(self, ctx:lfrXParser.AssignstatContext): + def exitAssignstat(self, ctx: lfrXParser.AssignstatContext): pass - # Enter a parse tree produced by lfrXParser#literalassignstat. - def enterLiteralassignstat(self, ctx:lfrXParser.LiteralassignstatContext): + def enterLiteralassignstat(self, ctx: lfrXParser.LiteralassignstatContext): pass # Exit a parse tree produced by lfrXParser#literalassignstat. - def exitLiteralassignstat(self, ctx:lfrXParser.LiteralassignstatContext): + def exitLiteralassignstat(self, ctx: lfrXParser.LiteralassignstatContext): pass - # Enter a parse tree produced by lfrXParser#bracketexpression. - def enterBracketexpression(self, ctx:lfrXParser.BracketexpressionContext): + def enterBracketexpression(self, ctx: lfrXParser.BracketexpressionContext): pass # Exit a parse tree produced by lfrXParser#bracketexpression. - def exitBracketexpression(self, ctx:lfrXParser.BracketexpressionContext): + def exitBracketexpression(self, ctx: lfrXParser.BracketexpressionContext): pass - # Enter a parse tree produced by lfrXParser#expression. - def enterExpression(self, ctx:lfrXParser.ExpressionContext): + def enterExpression(self, ctx: lfrXParser.ExpressionContext): pass # Exit a parse tree produced by lfrXParser#expression. - def exitExpression(self, ctx:lfrXParser.ExpressionContext): + def exitExpression(self, ctx: lfrXParser.ExpressionContext): pass - # Enter a parse tree produced by lfrXParser#expressionterm. - def enterExpressionterm(self, ctx:lfrXParser.ExpressiontermContext): + def enterExpressionterm(self, ctx: lfrXParser.ExpressiontermContext): pass # Exit a parse tree produced by lfrXParser#expressionterm. - def exitExpressionterm(self, ctx:lfrXParser.ExpressiontermContext): + def exitExpressionterm(self, ctx: lfrXParser.ExpressiontermContext): pass - # Enter a parse tree produced by lfrXParser#logiccondition_operand. - def enterLogiccondition_operand(self, ctx:lfrXParser.Logiccondition_operandContext): + def enterLogiccondition_operand( + self, ctx: lfrXParser.Logiccondition_operandContext + ): pass # Exit a parse tree produced by lfrXParser#logiccondition_operand. - def exitLogiccondition_operand(self, ctx:lfrXParser.Logiccondition_operandContext): + def exitLogiccondition_operand(self, ctx: lfrXParser.Logiccondition_operandContext): pass - # Enter a parse tree produced by lfrXParser#logiccondition. - def enterLogiccondition(self, ctx:lfrXParser.LogicconditionContext): + def enterLogiccondition(self, ctx: lfrXParser.LogicconditionContext): pass # Exit a parse tree produced by lfrXParser#logiccondition. - def exitLogiccondition(self, ctx:lfrXParser.LogicconditionContext): + def exitLogiccondition(self, ctx: lfrXParser.LogicconditionContext): pass - # Enter a parse tree produced by lfrXParser#logic_value. - def enterLogic_value(self, ctx:lfrXParser.Logic_valueContext): + def enterLogic_value(self, ctx: lfrXParser.Logic_valueContext): pass # Exit a parse tree produced by lfrXParser#logic_value. - def exitLogic_value(self, ctx:lfrXParser.Logic_valueContext): + def exitLogic_value(self, ctx: lfrXParser.Logic_valueContext): pass - # Enter a parse tree produced by lfrXParser#vector. - def enterVector(self, ctx:lfrXParser.VectorContext): + def enterVector(self, ctx: lfrXParser.VectorContext): pass # Exit a parse tree produced by lfrXParser#vector. - def exitVector(self, ctx:lfrXParser.VectorContext): + def exitVector(self, ctx: lfrXParser.VectorContext): pass - # Enter a parse tree produced by lfrXParser#variables. - def enterVariables(self, ctx:lfrXParser.VariablesContext): + def enterVariables(self, ctx: lfrXParser.VariablesContext): pass # Exit a parse tree produced by lfrXParser#variables. - def exitVariables(self, ctx:lfrXParser.VariablesContext): + def exitVariables(self, ctx: lfrXParser.VariablesContext): pass - # Enter a parse tree produced by lfrXParser#concatenation. - def enterConcatenation(self, ctx:lfrXParser.ConcatenationContext): + def enterConcatenation(self, ctx: lfrXParser.ConcatenationContext): pass # Exit a parse tree produced by lfrXParser#concatenation. - def exitConcatenation(self, ctx:lfrXParser.ConcatenationContext): + def exitConcatenation(self, ctx: lfrXParser.ConcatenationContext): pass - # Enter a parse tree produced by lfrXParser#lhs. - def enterLhs(self, ctx:lfrXParser.LhsContext): + def enterLhs(self, ctx: lfrXParser.LhsContext): pass # Exit a parse tree produced by lfrXParser#lhs. - def exitLhs(self, ctx:lfrXParser.LhsContext): + def exitLhs(self, ctx: lfrXParser.LhsContext): pass - # Enter a parse tree produced by lfrXParser#ioassignstat. - def enterIoassignstat(self, ctx:lfrXParser.IoassignstatContext): + def enterIoassignstat(self, ctx: lfrXParser.IoassignstatContext): pass # Exit a parse tree produced by lfrXParser#ioassignstat. - def exitIoassignstat(self, ctx:lfrXParser.IoassignstatContext): + def exitIoassignstat(self, ctx: lfrXParser.IoassignstatContext): pass - # Enter a parse tree produced by lfrXParser#technologydirectives. - def enterTechnologydirectives(self, ctx:lfrXParser.TechnologydirectivesContext): + def enterTechnologydirectives(self, ctx: lfrXParser.TechnologydirectivesContext): pass # Exit a parse tree produced by lfrXParser#technologydirectives. - def exitTechnologydirectives(self, ctx:lfrXParser.TechnologydirectivesContext): + def exitTechnologydirectives(self, ctx: lfrXParser.TechnologydirectivesContext): pass - # Enter a parse tree produced by lfrXParser#technologymappingdirective. - def enterTechnologymappingdirective(self, ctx:lfrXParser.TechnologymappingdirectiveContext): + def enterTechnologymappingdirective( + self, ctx: lfrXParser.TechnologymappingdirectiveContext + ): pass # Exit a parse tree produced by lfrXParser#technologymappingdirective. - def exitTechnologymappingdirective(self, ctx:lfrXParser.TechnologymappingdirectiveContext): + def exitTechnologymappingdirective( + self, ctx: lfrXParser.TechnologymappingdirectiveContext + ): pass - # Enter a parse tree produced by lfrXParser#materialmappingdirective. - def enterMaterialmappingdirective(self, ctx:lfrXParser.MaterialmappingdirectiveContext): + def enterMaterialmappingdirective( + self, ctx: lfrXParser.MaterialmappingdirectiveContext + ): pass # Exit a parse tree produced by lfrXParser#materialmappingdirective. - def exitMaterialmappingdirective(self, ctx:lfrXParser.MaterialmappingdirectiveContext): + def exitMaterialmappingdirective( + self, ctx: lfrXParser.MaterialmappingdirectiveContext + ): pass - # Enter a parse tree produced by lfrXParser#mappingoperator. - def enterMappingoperator(self, ctx:lfrXParser.MappingoperatorContext): + def enterMappingoperator(self, ctx: lfrXParser.MappingoperatorContext): pass # Exit a parse tree produced by lfrXParser#mappingoperator. - def exitMappingoperator(self, ctx:lfrXParser.MappingoperatorContext): + def exitMappingoperator(self, ctx: lfrXParser.MappingoperatorContext): pass - # Enter a parse tree produced by lfrXParser#performancedirective. - def enterPerformancedirective(self, ctx:lfrXParser.PerformancedirectiveContext): + def enterPerformancedirective(self, ctx: lfrXParser.PerformancedirectiveContext): pass # Exit a parse tree produced by lfrXParser#performancedirective. - def exitPerformancedirective(self, ctx:lfrXParser.PerformancedirectiveContext): + def exitPerformancedirective(self, ctx: lfrXParser.PerformancedirectiveContext): pass - # Enter a parse tree produced by lfrXParser#constraint. - def enterConstraint(self, ctx:lfrXParser.ConstraintContext): + def enterConstraint(self, ctx: lfrXParser.ConstraintContext): pass # Exit a parse tree produced by lfrXParser#constraint. - def exitConstraint(self, ctx:lfrXParser.ConstraintContext): + def exitConstraint(self, ctx: lfrXParser.ConstraintContext): pass - # Enter a parse tree produced by lfrXParser#unit. - def enterUnit(self, ctx:lfrXParser.UnitContext): + def enterUnit(self, ctx: lfrXParser.UnitContext): pass # Exit a parse tree produced by lfrXParser#unit. - def exitUnit(self, ctx:lfrXParser.UnitContext): + def exitUnit(self, ctx: lfrXParser.UnitContext): pass - # Enter a parse tree produced by lfrXParser#unary_operator. - def enterUnary_operator(self, ctx:lfrXParser.Unary_operatorContext): + def enterUnary_operator(self, ctx: lfrXParser.Unary_operatorContext): pass # Exit a parse tree produced by lfrXParser#unary_operator. - def exitUnary_operator(self, ctx:lfrXParser.Unary_operatorContext): + def exitUnary_operator(self, ctx: lfrXParser.Unary_operatorContext): pass - # Enter a parse tree produced by lfrXParser#binary_operator. - def enterBinary_operator(self, ctx:lfrXParser.Binary_operatorContext): + def enterBinary_operator(self, ctx: lfrXParser.Binary_operatorContext): pass # Exit a parse tree produced by lfrXParser#binary_operator. - def exitBinary_operator(self, ctx:lfrXParser.Binary_operatorContext): + def exitBinary_operator(self, ctx: lfrXParser.Binary_operatorContext): pass - # Enter a parse tree produced by lfrXParser#unary_module_path_operator. - def enterUnary_module_path_operator(self, ctx:lfrXParser.Unary_module_path_operatorContext): + def enterUnary_module_path_operator( + self, ctx: lfrXParser.Unary_module_path_operatorContext + ): pass # Exit a parse tree produced by lfrXParser#unary_module_path_operator. - def exitUnary_module_path_operator(self, ctx:lfrXParser.Unary_module_path_operatorContext): + def exitUnary_module_path_operator( + self, ctx: lfrXParser.Unary_module_path_operatorContext + ): pass - # Enter a parse tree produced by lfrXParser#binary_module_path_operator. - def enterBinary_module_path_operator(self, ctx:lfrXParser.Binary_module_path_operatorContext): + def enterBinary_module_path_operator( + self, ctx: lfrXParser.Binary_module_path_operatorContext + ): pass # Exit a parse tree produced by lfrXParser#binary_module_path_operator. - def exitBinary_module_path_operator(self, ctx:lfrXParser.Binary_module_path_operatorContext): + def exitBinary_module_path_operator( + self, ctx: lfrXParser.Binary_module_path_operatorContext + ): pass - # Enter a parse tree produced by lfrXParser#number. - def enterNumber(self, ctx:lfrXParser.NumberContext): + def enterNumber(self, ctx: lfrXParser.NumberContext): pass # Exit a parse tree produced by lfrXParser#number. - def exitNumber(self, ctx:lfrXParser.NumberContext): + def exitNumber(self, ctx: lfrXParser.NumberContext): pass - -del lfrXParser \ No newline at end of file +del lfrXParser diff --git a/lfr/antlrgen/lfr/lfrXParser.py b/lfr/antlrgen/lfr/lfrXParser.py index 386bc6e..9cf5d05 100755 --- a/lfr/antlrgen/lfr/lfrXParser.py +++ b/lfr/antlrgen/lfr/lfrXParser.py @@ -1,336 +1,5236 @@ -# Generated from ./lfrX.g4 by ANTLR 4.9.1 +# Generated from ./lfrX.g4 by ANTLR 4.10.1 # encoding: utf-8 -from antlr4 import * -from io import StringIO import sys +from io import StringIO + +from antlr4 import * + if sys.version_info[1] > 5: - from typing import TextIO + from typing import TextIO else: - from typing.io import TextIO + from typing.io import TextIO def serializedATN(): - with StringIO() as buf: - buf.write("\3\u608b\ua72a\u8133\ub9ed\u417c\u3be7\u7786\u5964\3M") - buf.write("\u0253\4\2\t\2\4\3\t\3\4\4\t\4\4\5\t\5\4\6\t\6\4\7\t\7") - buf.write("\4\b\t\b\4\t\t\t\4\n\t\n\4\13\t\13\4\f\t\f\4\r\t\r\4\16") - buf.write("\t\16\4\17\t\17\4\20\t\20\4\21\t\21\4\22\t\22\4\23\t\23") - buf.write("\4\24\t\24\4\25\t\25\4\26\t\26\4\27\t\27\4\30\t\30\4\31") - buf.write("\t\31\4\32\t\32\4\33\t\33\4\34\t\34\4\35\t\35\4\36\t\36") - buf.write("\4\37\t\37\4 \t \4!\t!\4\"\t\"\4#\t#\4$\t$\4%\t%\4&\t") - buf.write("&\4\'\t\'\4(\t(\4)\t)\4*\t*\4+\t+\4,\t,\4-\t-\4.\t.\4") - buf.write("/\t/\4\60\t\60\4\61\t\61\4\62\t\62\4\63\t\63\4\64\t\64") - buf.write("\4\65\t\65\4\66\t\66\4\67\t\67\48\t8\49\t9\4:\t:\4;\t") - buf.write(";\4<\t<\4=\t=\4>\t>\4?\t?\4@\t@\4A\tA\4B\tB\3\2\6\2\u0086") - buf.write("\n\2\r\2\16\2\u0087\3\3\3\3\3\3\3\3\3\4\3\4\3\4\3\4\3") - buf.write("\4\3\4\5\4\u0094\n\4\3\4\3\4\3\5\3\5\6\5\u009a\n\5\r\5") - buf.write("\16\5\u009b\3\6\3\6\3\6\7\6\u00a1\n\6\f\6\16\6\u00a4\13") - buf.write("\6\3\6\3\6\3\6\7\6\u00a9\n\6\f\6\16\6\u00ac\13\6\5\6\u00ae") - buf.write("\n\6\3\7\3\7\5\7\u00b2\n\7\3\b\3\b\3\b\3\b\7\b\u00b8\n") - buf.write("\b\f\b\16\b\u00bb\13\b\3\b\3\b\3\b\3\b\7\b\u00c1\n\b\f") - buf.write("\b\16\b\u00c4\13\b\3\b\3\b\3\b\3\b\7\b\u00ca\n\b\f\b\16") - buf.write("\b\u00cd\13\b\5\b\u00cf\n\b\3\t\5\t\u00d2\n\t\3\t\3\t") - buf.write("\3\n\3\n\3\n\3\n\3\n\3\n\3\n\3\n\3\13\6\13\u00df\n\13") - buf.write("\r\13\16\13\u00e0\3\f\3\f\3\f\5\f\u00e6\n\f\3\r\3\r\7") - buf.write("\r\u00ea\n\r\f\r\16\r\u00ed\13\r\3\r\5\r\u00f0\n\r\3\16") - buf.write("\3\16\3\16\3\16\3\16\3\16\3\17\3\17\3\17\3\20\3\20\3\20") - buf.write("\3\20\3\20\3\20\3\20\3\21\3\21\3\21\3\21\3\22\3\22\6\22") - buf.write("\u0108\n\22\r\22\16\22\u0109\3\22\3\22\3\22\5\22\u010f") - buf.write("\n\22\3\23\3\23\6\23\u0113\n\23\r\23\16\23\u0114\3\23") - buf.write("\5\23\u0118\n\23\3\23\3\23\3\24\3\24\3\24\3\24\3\24\3") - buf.write("\25\3\25\3\25\3\25\3\26\3\26\3\26\3\26\3\27\3\27\3\30") - buf.write("\3\30\3\30\3\30\3\30\5\30\u0130\n\30\3\30\3\30\3\31\3") - buf.write("\31\3\31\7\31\u0137\n\31\f\31\16\31\u013a\13\31\3\32\3") - buf.write("\32\5\32\u013e\n\32\3\33\3\33\3\33\3\33\5\33\u0144\n\33") - buf.write("\3\34\3\34\3\34\3\34\3\34\5\34\u014b\n\34\3\35\3\35\3") - buf.write("\35\3\35\3\35\3\35\3\36\3\36\5\36\u0155\n\36\3\37\3\37") - buf.write("\3\37\7\37\u015a\n\37\f\37\16\37\u015d\13\37\3 \3 \3 ") - buf.write("\7 \u0162\n \f \16 \u0165\13 \3!\3!\3!\3!\3!\3!\3\"\3") - buf.write("\"\3#\3#\3$\3$\3$\3$\3$\5$\u0176\n$\3%\3%\3%\3%\7%\u017c") - buf.write("\n%\f%\16%\u017f\13%\3&\3&\3&\3&\7&\u0185\n&\f&\16&\u0188") - buf.write("\13&\3\'\3\'\3\'\3\'\7\'\u018e\n\'\f\'\16\'\u0191\13\'") - buf.write("\3(\3(\3(\3(\7(\u0197\n(\f(\16(\u019a\13(\3)\3)\3)\3)") - buf.write("\7)\u01a0\n)\f)\16)\u01a3\13)\3*\3*\3*\3*\3*\5*\u01aa") - buf.write("\n*\3+\3+\3+\3+\5+\u01b0\n+\3,\5,\u01b3\n,\3,\3,\3,\3") - buf.write(",\3-\3-\5-\u01bb\n-\3-\3-\3-\5-\u01c0\n-\7-\u01c2\n-\f") - buf.write("-\16-\u01c5\13-\3.\5.\u01c8\n.\3.\3.\5.\u01cc\n.\3/\3") - buf.write("/\5/\u01d0\n/\3/\3/\3/\5/\u01d5\n/\7/\u01d7\n/\f/\16/") - buf.write("\u01da\13/\3\60\3\60\3\60\3\60\3\61\3\61\3\62\3\62\3\62") - buf.write("\3\62\5\62\u01e6\n\62\3\62\3\62\3\63\3\63\5\63\u01ec\n") - buf.write("\63\3\64\3\64\3\64\3\64\7\64\u01f2\n\64\f\64\16\64\u01f5") - buf.write("\13\64\3\64\3\64\5\64\u01f9\n\64\3\65\3\65\3\66\3\66\3") - buf.write("\67\3\67\3\67\5\67\u0202\n\67\38\38\38\68\u0207\n8\r8") - buf.write("\168\u0208\38\38\38\38\58\u020f\n8\38\38\39\39\39\39\3") - buf.write(":\3:\5:\u0219\n:\3;\3;\3;\3;\3;\3;\3;\7;\u0222\n;\f;\16") - buf.write(";\u0225\13;\3<\3<\3<\3<\5<\u022b\n<\3<\3<\3<\3<\5<\u0231") - buf.write("\n<\3<\3<\3<\3<\5<\u0237\n<\3<\3<\3<\3<\5<\u023d\n<\3") - buf.write("<\3<\3<\3<\5<\u0243\n<\5<\u0245\n<\3=\3=\3>\3>\3?\3?\3") - buf.write("@\3@\3A\3A\3B\3B\3B\2\2C\2\4\6\b\n\f\16\20\22\24\26\30") - buf.write("\32\34\36 \"$&(*,.\60\62\64\668:<>@BDFHJLNPRTVXZ\\^`b") - buf.write("dfhjlnprtvxz|~\u0080\u0082\2\t\4\2\31\32\34\34\3\2&\'") - buf.write("\3\2+\65\7\2\25\25(,//\61\61\63C\3\2-\65\7\2//\61\61\63") - buf.write("\659:=>\3\2IM\2\u025b\2\u0085\3\2\2\2\4\u0089\3\2\2\2") - buf.write("\6\u008d\3\2\2\2\b\u0099\3\2\2\2\n\u00ad\3\2\2\2\f\u00af") - buf.write("\3\2\2\2\16\u00ce\3\2\2\2\20\u00d1\3\2\2\2\22\u00d5\3") - buf.write("\2\2\2\24\u00de\3\2\2\2\26\u00e5\3\2\2\2\30\u00e7\3\2") - buf.write("\2\2\32\u00f1\3\2\2\2\34\u00f7\3\2\2\2\36\u00fa\3\2\2") - buf.write("\2 \u0101\3\2\2\2\"\u010e\3\2\2\2$\u0110\3\2\2\2&\u011b") - buf.write("\3\2\2\2(\u0120\3\2\2\2*\u0124\3\2\2\2,\u0128\3\2\2\2") - buf.write(".\u012a\3\2\2\2\60\u0133\3\2\2\2\62\u013b\3\2\2\2\64\u0143") - buf.write("\3\2\2\2\66\u014a\3\2\2\28\u014c\3\2\2\2:\u0154\3\2\2") - buf.write("\2<\u0156\3\2\2\2>\u015e\3\2\2\2@\u0166\3\2\2\2B\u016c") - buf.write("\3\2\2\2D\u016e\3\2\2\2F\u0175\3\2\2\2H\u0177\3\2\2\2") - buf.write("J\u0180\3\2\2\2L\u0189\3\2\2\2N\u0192\3\2\2\2P\u019b\3") - buf.write("\2\2\2R\u01a4\3\2\2\2T\u01ab\3\2\2\2V\u01b2\3\2\2\2X\u01ba") - buf.write("\3\2\2\2Z\u01cb\3\2\2\2\\\u01cf\3\2\2\2^\u01db\3\2\2\2") - buf.write("`\u01df\3\2\2\2b\u01e1\3\2\2\2d\u01eb\3\2\2\2f\u01ed\3") - buf.write("\2\2\2h\u01fa\3\2\2\2j\u01fc\3\2\2\2l\u0201\3\2\2\2n\u0203") - buf.write("\3\2\2\2p\u0212\3\2\2\2r\u0218\3\2\2\2t\u021a\3\2\2\2") - buf.write("v\u0244\3\2\2\2x\u0246\3\2\2\2z\u0248\3\2\2\2|\u024a\3") - buf.write("\2\2\2~\u024c\3\2\2\2\u0080\u024e\3\2\2\2\u0082\u0250") - buf.write("\3\2\2\2\u0084\u0086\5\4\3\2\u0085\u0084\3\2\2\2\u0086") - buf.write("\u0087\3\2\2\2\u0087\u0085\3\2\2\2\u0087\u0088\3\2\2\2") - buf.write("\u0088\3\3\2\2\2\u0089\u008a\5\6\4\2\u008a\u008b\5\b\5") - buf.write("\2\u008b\u008c\7\3\2\2\u008c\5\3\2\2\2\u008d\u008e\7\4") - buf.write("\2\2\u008e\u0093\7D\2\2\u008f\u0090\7\5\2\2\u0090\u0091") - buf.write("\5\n\6\2\u0091\u0092\7\6\2\2\u0092\u0094\3\2\2\2\u0093") - buf.write("\u008f\3\2\2\2\u0093\u0094\3\2\2\2\u0094\u0095\3\2\2\2") - buf.write("\u0095\u0096\7\7\2\2\u0096\7\3\2\2\2\u0097\u009a\5\64") - buf.write("\33\2\u0098\u009a\5\22\n\2\u0099\u0097\3\2\2\2\u0099\u0098") - buf.write("\3\2\2\2\u009a\u009b\3\2\2\2\u009b\u0099\3\2\2\2\u009b") - buf.write("\u009c\3\2\2\2\u009c\t\3\2\2\2\u009d\u00a2\5\f\7\2\u009e") - buf.write("\u009f\7\b\2\2\u009f\u00a1\5\f\7\2\u00a0\u009e\3\2\2\2") - buf.write("\u00a1\u00a4\3\2\2\2\u00a2\u00a0\3\2\2\2\u00a2\u00a3\3") - buf.write("\2\2\2\u00a3\u00ae\3\2\2\2\u00a4\u00a2\3\2\2\2\u00a5\u00aa") - buf.write("\5\16\b\2\u00a6\u00a7\7\b\2\2\u00a7\u00a9\5\16\b\2\u00a8") - buf.write("\u00a6\3\2\2\2\u00a9\u00ac\3\2\2\2\u00aa\u00a8\3\2\2\2") - buf.write("\u00aa\u00ab\3\2\2\2\u00ab\u00ae\3\2\2\2\u00ac\u00aa\3") - buf.write("\2\2\2\u00ad\u009d\3\2\2\2\u00ad\u00a5\3\2\2\2\u00ae\13") - buf.write("\3\2\2\2\u00af\u00b1\7D\2\2\u00b0\u00b2\5b\62\2\u00b1") - buf.write("\u00b0\3\2\2\2\u00b1\u00b2\3\2\2\2\u00b2\r\3\2\2\2\u00b3") - buf.write("\u00b4\7\t\2\2\u00b4\u00b9\5\20\t\2\u00b5\u00b6\7\b\2") - buf.write("\2\u00b6\u00b8\5\20\t\2\u00b7\u00b5\3\2\2\2\u00b8\u00bb") - buf.write("\3\2\2\2\u00b9\u00b7\3\2\2\2\u00b9\u00ba\3\2\2\2\u00ba") - buf.write("\u00cf\3\2\2\2\u00bb\u00b9\3\2\2\2\u00bc\u00bd\7\n\2\2") - buf.write("\u00bd\u00c2\5\20\t\2\u00be\u00bf\7\b\2\2\u00bf\u00c1") - buf.write("\5\20\t\2\u00c0\u00be\3\2\2\2\u00c1\u00c4\3\2\2\2\u00c2") - buf.write("\u00c0\3\2\2\2\u00c2\u00c3\3\2\2\2\u00c3\u00cf\3\2\2\2") - buf.write("\u00c4\u00c2\3\2\2\2\u00c5\u00c6\7\13\2\2\u00c6\u00cb") - buf.write("\5\20\t\2\u00c7\u00c8\7\b\2\2\u00c8\u00ca\5\20\t\2\u00c9") - buf.write("\u00c7\3\2\2\2\u00ca\u00cd\3\2\2\2\u00cb\u00c9\3\2\2\2") - buf.write("\u00cb\u00cc\3\2\2\2\u00cc\u00cf\3\2\2\2\u00cd\u00cb\3") - buf.write("\2\2\2\u00ce\u00b3\3\2\2\2\u00ce\u00bc\3\2\2\2\u00ce\u00c5") - buf.write("\3\2\2\2\u00cf\17\3\2\2\2\u00d0\u00d2\5b\62\2\u00d1\u00d0") - buf.write("\3\2\2\2\u00d1\u00d2\3\2\2\2\u00d2\u00d3\3\2\2\2\u00d3") - buf.write("\u00d4\7D\2\2\u00d4\21\3\2\2\2\u00d5\u00d6\7\f\2\2\u00d6") - buf.write("\u00d7\7\5\2\2\u00d7\u00d8\5\60\31\2\u00d8\u00d9\7\6\2") - buf.write("\2\u00d9\u00da\7\r\2\2\u00da\u00db\5\24\13\2\u00db\u00dc") - buf.write("\7\16\2\2\u00dc\23\3\2\2\2\u00dd\u00df\5\26\f\2\u00de") - buf.write("\u00dd\3\2\2\2\u00df\u00e0\3\2\2\2\u00e0\u00de\3\2\2\2") - buf.write("\u00e0\u00e1\3\2\2\2\u00e1\25\3\2\2\2\u00e2\u00e6\5.\30") - buf.write("\2\u00e3\u00e6\5$\23\2\u00e4\u00e6\5\30\r\2\u00e5\u00e2") - buf.write("\3\2\2\2\u00e5\u00e3\3\2\2\2\u00e5\u00e4\3\2\2\2\u00e6") - buf.write("\27\3\2\2\2\u00e7\u00eb\5\32\16\2\u00e8\u00ea\5\36\20") - buf.write("\2\u00e9\u00e8\3\2\2\2\u00ea\u00ed\3\2\2\2\u00eb\u00e9") - buf.write("\3\2\2\2\u00eb\u00ec\3\2\2\2\u00ec\u00ef\3\2\2\2\u00ed") - buf.write("\u00eb\3\2\2\2\u00ee\u00f0\5\34\17\2\u00ef\u00ee\3\2\2") - buf.write("\2\u00ef\u00f0\3\2\2\2\u00f0\31\3\2\2\2\u00f1\u00f2\7") - buf.write("\17\2\2\u00f2\u00f3\7\5\2\2\u00f3\u00f4\5 \21\2\u00f4") - buf.write("\u00f5\7\6\2\2\u00f5\u00f6\5\"\22\2\u00f6\33\3\2\2\2\u00f7") - buf.write("\u00f8\7\20\2\2\u00f8\u00f9\5\"\22\2\u00f9\35\3\2\2\2") - buf.write("\u00fa\u00fb\7\20\2\2\u00fb\u00fc\7\17\2\2\u00fc\u00fd") - buf.write("\7\5\2\2\u00fd\u00fe\5 \21\2\u00fe\u00ff\7\6\2\2\u00ff") - buf.write("\u0100\5\"\22\2\u0100\37\3\2\2\2\u0101\u0102\5h\65\2\u0102") - buf.write("\u0103\5\u0080A\2\u0103\u0104\5,\27\2\u0104!\3\2\2\2\u0105") - buf.write("\u0107\7\r\2\2\u0106\u0108\5.\30\2\u0107\u0106\3\2\2\2") - buf.write("\u0108\u0109\3\2\2\2\u0109\u0107\3\2\2\2\u0109\u010a\3") - buf.write("\2\2\2\u010a\u010b\3\2\2\2\u010b\u010c\7\16\2\2\u010c") - buf.write("\u010f\3\2\2\2\u010d\u010f\5.\30\2\u010e\u0105\3\2\2\2") - buf.write("\u010e\u010d\3\2\2\2\u010f#\3\2\2\2\u0110\u0112\5&\24") - buf.write("\2\u0111\u0113\5(\25\2\u0112\u0111\3\2\2\2\u0113\u0114") - buf.write("\3\2\2\2\u0114\u0112\3\2\2\2\u0114\u0115\3\2\2\2\u0115") - buf.write("\u0117\3\2\2\2\u0116\u0118\5*\26\2\u0117\u0116\3\2\2\2") - buf.write("\u0117\u0118\3\2\2\2\u0118\u0119\3\2\2\2\u0119\u011a\7") - buf.write("\21\2\2\u011a%\3\2\2\2\u011b\u011c\7\22\2\2\u011c\u011d") - buf.write("\7\5\2\2\u011d\u011e\5h\65\2\u011e\u011f\7\6\2\2\u011f") - buf.write("\'\3\2\2\2\u0120\u0121\5,\27\2\u0121\u0122\7\23\2\2\u0122") - buf.write("\u0123\5\"\22\2\u0123)\3\2\2\2\u0124\u0125\7\24\2\2\u0125") - buf.write("\u0126\7\23\2\2\u0126\u0127\5\"\22\2\u0127+\3\2\2\2\u0128") - buf.write("\u0129\5\u0082B\2\u0129-\3\2\2\2\u012a\u012b\5h\65\2\u012b") - buf.write("\u012f\7\25\2\2\u012c\u0130\5\u0082B\2\u012d\u0130\5d") - buf.write("\63\2\u012e\u0130\5X-\2\u012f\u012c\3\2\2\2\u012f\u012d") - buf.write("\3\2\2\2\u012f\u012e\3\2\2\2\u0130\u0131\3\2\2\2\u0131") - buf.write("\u0132\7\7\2\2\u0132/\3\2\2\2\u0133\u0138\5\62\32\2\u0134") - buf.write("\u0135\7\b\2\2\u0135\u0137\5\62\32\2\u0136\u0134\3\2\2") - buf.write("\2\u0137\u013a\3\2\2\2\u0138\u0136\3\2\2\2\u0138\u0139") - buf.write("\3\2\2\2\u0139\61\3\2\2\2\u013a\u0138\3\2\2\2\u013b\u013d") - buf.write("\7D\2\2\u013c\u013e\5b\62\2\u013d\u013c\3\2\2\2\u013d") - buf.write("\u013e\3\2\2\2\u013e\63\3\2\2\2\u013f\u0140\5\66\34\2") - buf.write("\u0140\u0141\7\7\2\2\u0141\u0144\3\2\2\2\u0142\u0144\5") - buf.write("l\67\2\u0143\u013f\3\2\2\2\u0143\u0142\3\2\2\2\u0144\65") - buf.write("\3\2\2\2\u0145\u014b\5j\66\2\u0146\u014b\5R*\2\u0147\u014b") - buf.write("\5F$\2\u0148\u014b\5T+\2\u0149\u014b\58\35\2\u014a\u0145") - buf.write("\3\2\2\2\u014a\u0146\3\2\2\2\u014a\u0147\3\2\2\2\u014a") - buf.write("\u0148\3\2\2\2\u014a\u0149\3\2\2\2\u014b\67\3\2\2\2\u014c") - buf.write("\u014d\5D#\2\u014d\u014e\5B\"\2\u014e\u014f\7\5\2\2\u014f") - buf.write("\u0150\5:\36\2\u0150\u0151\7\6\2\2\u01519\3\2\2\2\u0152") - buf.write("\u0155\5<\37\2\u0153\u0155\5> \2\u0154\u0152\3\2\2\2\u0154") - buf.write("\u0153\3\2\2\2\u0155;\3\2\2\2\u0156\u015b\5\f\7\2\u0157") - buf.write("\u0158\7\b\2\2\u0158\u015a\5\f\7\2\u0159\u0157\3\2\2\2") - buf.write("\u015a\u015d\3\2\2\2\u015b\u0159\3\2\2\2\u015b\u015c\3") - buf.write("\2\2\2\u015c=\3\2\2\2\u015d\u015b\3\2\2\2\u015e\u0163") - buf.write("\5@!\2\u015f\u0160\7\b\2\2\u0160\u0162\5@!\2\u0161\u015f") - buf.write("\3\2\2\2\u0162\u0165\3\2\2\2\u0163\u0161\3\2\2\2\u0163") - buf.write("\u0164\3\2\2\2\u0164?\3\2\2\2\u0165\u0163\3\2\2\2\u0166") - buf.write("\u0167\7\26\2\2\u0167\u0168\7D\2\2\u0168\u0169\7\5\2\2") - buf.write("\u0169\u016a\5d\63\2\u016a\u016b\7\6\2\2\u016bA\3\2\2") - buf.write("\2\u016c\u016d\7D\2\2\u016dC\3\2\2\2\u016e\u016f\7D\2") - buf.write("\2\u016fE\3\2\2\2\u0170\u0176\5J&\2\u0171\u0176\5L\'\2") - buf.write("\u0172\u0176\5P)\2\u0173\u0176\5H%\2\u0174\u0176\5N(\2") - buf.write("\u0175\u0170\3\2\2\2\u0175\u0171\3\2\2\2\u0175\u0172\3") - buf.write("\2\2\2\u0175\u0173\3\2\2\2\u0175\u0174\3\2\2\2\u0176G") - buf.write("\3\2\2\2\u0177\u0178\7\27\2\2\u0178\u017d\5\20\t\2\u0179") - buf.write("\u017a\7\b\2\2\u017a\u017c\5\20\t\2\u017b\u0179\3\2\2") - buf.write("\2\u017c\u017f\3\2\2\2\u017d\u017b\3\2\2\2\u017d\u017e") - buf.write("\3\2\2\2\u017eI\3\2\2\2\u017f\u017d\3\2\2\2\u0180\u0181") - buf.write("\7\30\2\2\u0181\u0186\5\20\t\2\u0182\u0183\7\b\2\2\u0183") - buf.write("\u0185\5\20\t\2\u0184\u0182\3\2\2\2\u0185\u0188\3\2\2") - buf.write("\2\u0186\u0184\3\2\2\2\u0186\u0187\3\2\2\2\u0187K\3\2") - buf.write("\2\2\u0188\u0186\3\2\2\2\u0189\u018a\7\31\2\2\u018a\u018f") - buf.write("\5\20\t\2\u018b\u018c\7\b\2\2\u018c\u018e\5\20\t\2\u018d") - buf.write("\u018b\3\2\2\2\u018e\u0191\3\2\2\2\u018f\u018d\3\2\2\2") - buf.write("\u018f\u0190\3\2\2\2\u0190M\3\2\2\2\u0191\u018f\3\2\2") - buf.write("\2\u0192\u0193\7\32\2\2\u0193\u0198\5\20\t\2\u0194\u0195") - buf.write("\7\b\2\2\u0195\u0197\5\20\t\2\u0196\u0194\3\2\2\2\u0197") - buf.write("\u019a\3\2\2\2\u0198\u0196\3\2\2\2\u0198\u0199\3\2\2\2") - buf.write("\u0199O\3\2\2\2\u019a\u0198\3\2\2\2\u019b\u019c\7\33\2") - buf.write("\2\u019c\u01a1\5T+\2\u019d\u019e\7\b\2\2\u019e\u01a0\5") - buf.write("T+\2\u019f\u019d\3\2\2\2\u01a0\u01a3\3\2\2\2\u01a1\u019f") - buf.write("\3\2\2\2\u01a1\u01a2\3\2\2\2\u01a2Q\3\2\2\2\u01a3\u01a1") - buf.write("\3\2\2\2\u01a4\u01a5\7\34\2\2\u01a5\u01a6\5h\65\2\u01a6") - buf.write("\u01a9\7\35\2\2\u01a7\u01aa\5V,\2\u01a8\u01aa\5X-\2\u01a9") - buf.write("\u01a7\3\2\2\2\u01a9\u01a8\3\2\2\2\u01aaS\3\2\2\2\u01ab") - buf.write("\u01ac\7D\2\2\u01ac\u01af\7\35\2\2\u01ad\u01b0\5V,\2\u01ae") - buf.write("\u01b0\5X-\2\u01af\u01ad\3\2\2\2\u01af\u01ae\3\2\2\2\u01b0") - buf.write("U\3\2\2\2\u01b1\u01b3\5z>\2\u01b2\u01b1\3\2\2\2\u01b2") - buf.write("\u01b3\3\2\2\2\u01b3\u01b4\3\2\2\2\u01b4\u01b5\7\5\2\2") - buf.write("\u01b5\u01b6\5X-\2\u01b6\u01b7\7\6\2\2\u01b7W\3\2\2\2") - buf.write("\u01b8\u01bb\5V,\2\u01b9\u01bb\5Z.\2\u01ba\u01b8\3\2\2") - buf.write("\2\u01ba\u01b9\3\2\2\2\u01bb\u01c3\3\2\2\2\u01bc\u01bf") - buf.write("\5|?\2\u01bd\u01c0\5V,\2\u01be\u01c0\5Z.\2\u01bf\u01bd") - buf.write("\3\2\2\2\u01bf\u01be\3\2\2\2\u01c0\u01c2\3\2\2\2\u01c1") - buf.write("\u01bc\3\2\2\2\u01c2\u01c5\3\2\2\2\u01c3\u01c1\3\2\2\2") - buf.write("\u01c3\u01c4\3\2\2\2\u01c4Y\3\2\2\2\u01c5\u01c3\3\2\2") - buf.write("\2\u01c6\u01c8\5z>\2\u01c7\u01c6\3\2\2\2\u01c7\u01c8\3") - buf.write("\2\2\2\u01c8\u01c9\3\2\2\2\u01c9\u01cc\5d\63\2\u01ca\u01cc") - buf.write("\5\u0082B\2\u01cb\u01c7\3\2\2\2\u01cb\u01ca\3\2\2\2\u01cc") - buf.write("[\3\2\2\2\u01cd\u01d0\5V,\2\u01ce\u01d0\5Z.\2\u01cf\u01cd") - buf.write("\3\2\2\2\u01cf\u01ce\3\2\2\2\u01d0\u01d8\3\2\2\2\u01d1") - buf.write("\u01d4\5|?\2\u01d2\u01d5\5V,\2\u01d3\u01d5\5Z.\2\u01d4") - buf.write("\u01d2\3\2\2\2\u01d4\u01d3\3\2\2\2\u01d5\u01d7\3\2\2\2") - buf.write("\u01d6\u01d1\3\2\2\2\u01d7\u01da\3\2\2\2\u01d8\u01d6\3") - buf.write("\2\2\2\u01d8\u01d9\3\2\2\2\u01d9]\3\2\2\2\u01da\u01d8") - buf.write("\3\2\2\2\u01db\u01dc\5\\/\2\u01dc\u01dd\5\u0080A\2\u01dd") - buf.write("\u01de\5`\61\2\u01de_\3\2\2\2\u01df\u01e0\5\u0082B\2\u01e0") - buf.write("a\3\2\2\2\u01e1\u01e2\7\36\2\2\u01e2\u01e5\7J\2\2\u01e3") - buf.write("\u01e4\7\23\2\2\u01e4\u01e6\7J\2\2\u01e5\u01e3\3\2\2\2") - buf.write("\u01e5\u01e6\3\2\2\2\u01e6\u01e7\3\2\2\2\u01e7\u01e8\7") - buf.write("\37\2\2\u01e8c\3\2\2\2\u01e9\u01ec\5\f\7\2\u01ea\u01ec") - buf.write("\5f\64\2\u01eb\u01e9\3\2\2\2\u01eb\u01ea\3\2\2\2\u01ec") - buf.write("e\3\2\2\2\u01ed\u01ee\7 \2\2\u01ee\u01f3\5\f\7\2\u01ef") - buf.write("\u01f0\7\b\2\2\u01f0\u01f2\5\f\7\2\u01f1\u01ef\3\2\2\2") - buf.write("\u01f2\u01f5\3\2\2\2\u01f3\u01f1\3\2\2\2\u01f3\u01f4\3") - buf.write("\2\2\2\u01f4\u01f6\3\2\2\2\u01f5\u01f3\3\2\2\2\u01f6\u01f8") - buf.write("\7!\2\2\u01f7\u01f9\5b\62\2\u01f8\u01f7\3\2\2\2\u01f8") - buf.write("\u01f9\3\2\2\2\u01f9g\3\2\2\2\u01fa\u01fb\5d\63\2\u01fb") - buf.write("i\3\2\2\2\u01fc\u01fd\5\16\b\2\u01fdk\3\2\2\2\u01fe\u0202") - buf.write("\5t;\2\u01ff\u0202\5n8\2\u0200\u0202\5p9\2\u0201\u01fe") - buf.write("\3\2\2\2\u0201\u01ff\3\2\2\2\u0201\u0200\3\2\2\2\u0202") - buf.write("m\3\2\2\2\u0203\u0204\7\"\2\2\u0204\u0206\7#\2\2\u0205") - buf.write("\u0207\7D\2\2\u0206\u0205\3\2\2\2\u0207\u0208\3\2\2\2") - buf.write("\u0208\u0206\3\2\2\2\u0208\u0209\3\2\2\2\u0209\u020a\3") - buf.write("\2\2\2\u020a\u020b\7#\2\2\u020b\u020e\7#\2\2\u020c\u020f") - buf.write("\5r:\2\u020d\u020f\t\2\2\2\u020e\u020c\3\2\2\2\u020e\u020d") - buf.write("\3\2\2\2\u020f\u0210\3\2\2\2\u0210\u0211\7#\2\2\u0211") - buf.write("o\3\2\2\2\u0212\u0213\7$\2\2\u0213\u0214\7D\2\2\u0214") - buf.write("\u0215\7D\2\2\u0215q\3\2\2\2\u0216\u0219\5|?\2\u0217\u0219") - buf.write("\5z>\2\u0218\u0216\3\2\2\2\u0218\u0217\3\2\2\2\u0219s") - buf.write("\3\2\2\2\u021a\u021b\7%\2\2\u021b\u021c\7#\2\2\u021c\u021d") - buf.write("\5r:\2\u021d\u021e\7#\2\2\u021e\u0223\5v<\2\u021f\u0220") - buf.write("\t\3\2\2\u0220\u0222\5v<\2\u0221\u021f\3\2\2\2\u0222\u0225") - buf.write("\3\2\2\2\u0223\u0221\3\2\2\2\u0223\u0224\3\2\2\2\u0224") - buf.write("u\3\2\2\2\u0225\u0223\3\2\2\2\u0226\u0227\7D\2\2\u0227") - buf.write("\u0228\7\35\2\2\u0228\u022a\5\u0082B\2\u0229\u022b\5x") - buf.write("=\2\u022a\u0229\3\2\2\2\u022a\u022b\3\2\2\2\u022b\u0245") - buf.write("\3\2\2\2\u022c\u022d\7D\2\2\u022d\u022e\7(\2\2\u022e\u0230") - buf.write("\5\u0082B\2\u022f\u0231\5x=\2\u0230\u022f\3\2\2\2\u0230") - buf.write("\u0231\3\2\2\2\u0231\u0245\3\2\2\2\u0232\u0233\7D\2\2") - buf.write("\u0233\u0234\7)\2\2\u0234\u0236\5\u0082B\2\u0235\u0237") - buf.write("\5x=\2\u0236\u0235\3\2\2\2\u0236\u0237\3\2\2\2\u0237\u0245") - buf.write("\3\2\2\2\u0238\u0239\7D\2\2\u0239\u023a\7*\2\2\u023a\u023c") - buf.write("\5\u0082B\2\u023b\u023d\5x=\2\u023c\u023b\3\2\2\2\u023c") - buf.write("\u023d\3\2\2\2\u023d\u0245\3\2\2\2\u023e\u023f\7D\2\2") - buf.write("\u023f\u0240\7\25\2\2\u0240\u0242\5\u0082B\2\u0241\u0243") - buf.write("\5x=\2\u0242\u0241\3\2\2\2\u0242\u0243\3\2\2\2\u0243\u0245") - buf.write("\3\2\2\2\u0244\u0226\3\2\2\2\u0244\u022c\3\2\2\2\u0244") - buf.write("\u0232\3\2\2\2\u0244\u0238\3\2\2\2\u0244\u023e\3\2\2\2") - buf.write("\u0245w\3\2\2\2\u0246\u0247\7D\2\2\u0247y\3\2\2\2\u0248") - buf.write("\u0249\t\4\2\2\u0249{\3\2\2\2\u024a\u024b\t\5\2\2\u024b") - buf.write("}\3\2\2\2\u024c\u024d\t\6\2\2\u024d\177\3\2\2\2\u024e") - buf.write("\u024f\t\7\2\2\u024f\u0081\3\2\2\2\u0250\u0251\t\b\2\2") - buf.write("\u0251\u0083\3\2\2\2?\u0087\u0093\u0099\u009b\u00a2\u00aa") - buf.write("\u00ad\u00b1\u00b9\u00c2\u00cb\u00ce\u00d1\u00e0\u00e5") - buf.write("\u00eb\u00ef\u0109\u010e\u0114\u0117\u012f\u0138\u013d") - buf.write("\u0143\u014a\u0154\u015b\u0163\u0175\u017d\u0186\u018f") - buf.write("\u0198\u01a1\u01a9\u01af\u01b2\u01ba\u01bf\u01c3\u01c7") - buf.write("\u01cb\u01cf\u01d4\u01d8\u01e5\u01eb\u01f3\u01f8\u0201") - buf.write("\u0208\u020e\u0218\u0223\u022a\u0230\u0236\u023c\u0242") - buf.write("\u0244") - return buf.getvalue() - - -class lfrXParser ( Parser ): - + return [ + 4, + 1, + 75, + 593, + 2, + 0, + 7, + 0, + 2, + 1, + 7, + 1, + 2, + 2, + 7, + 2, + 2, + 3, + 7, + 3, + 2, + 4, + 7, + 4, + 2, + 5, + 7, + 5, + 2, + 6, + 7, + 6, + 2, + 7, + 7, + 7, + 2, + 8, + 7, + 8, + 2, + 9, + 7, + 9, + 2, + 10, + 7, + 10, + 2, + 11, + 7, + 11, + 2, + 12, + 7, + 12, + 2, + 13, + 7, + 13, + 2, + 14, + 7, + 14, + 2, + 15, + 7, + 15, + 2, + 16, + 7, + 16, + 2, + 17, + 7, + 17, + 2, + 18, + 7, + 18, + 2, + 19, + 7, + 19, + 2, + 20, + 7, + 20, + 2, + 21, + 7, + 21, + 2, + 22, + 7, + 22, + 2, + 23, + 7, + 23, + 2, + 24, + 7, + 24, + 2, + 25, + 7, + 25, + 2, + 26, + 7, + 26, + 2, + 27, + 7, + 27, + 2, + 28, + 7, + 28, + 2, + 29, + 7, + 29, + 2, + 30, + 7, + 30, + 2, + 31, + 7, + 31, + 2, + 32, + 7, + 32, + 2, + 33, + 7, + 33, + 2, + 34, + 7, + 34, + 2, + 35, + 7, + 35, + 2, + 36, + 7, + 36, + 2, + 37, + 7, + 37, + 2, + 38, + 7, + 38, + 2, + 39, + 7, + 39, + 2, + 40, + 7, + 40, + 2, + 41, + 7, + 41, + 2, + 42, + 7, + 42, + 2, + 43, + 7, + 43, + 2, + 44, + 7, + 44, + 2, + 45, + 7, + 45, + 2, + 46, + 7, + 46, + 2, + 47, + 7, + 47, + 2, + 48, + 7, + 48, + 2, + 49, + 7, + 49, + 2, + 50, + 7, + 50, + 2, + 51, + 7, + 51, + 2, + 52, + 7, + 52, + 2, + 53, + 7, + 53, + 2, + 54, + 7, + 54, + 2, + 55, + 7, + 55, + 2, + 56, + 7, + 56, + 2, + 57, + 7, + 57, + 2, + 58, + 7, + 58, + 2, + 59, + 7, + 59, + 2, + 60, + 7, + 60, + 2, + 61, + 7, + 61, + 2, + 62, + 7, + 62, + 2, + 63, + 7, + 63, + 2, + 64, + 7, + 64, + 1, + 0, + 4, + 0, + 132, + 8, + 0, + 11, + 0, + 12, + 0, + 133, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 2, + 1, + 2, + 1, + 2, + 1, + 2, + 1, + 2, + 3, + 2, + 146, + 8, + 2, + 1, + 2, + 1, + 2, + 1, + 3, + 1, + 3, + 4, + 3, + 152, + 8, + 3, + 11, + 3, + 12, + 3, + 153, + 1, + 4, + 1, + 4, + 1, + 4, + 5, + 4, + 159, + 8, + 4, + 10, + 4, + 12, + 4, + 162, + 9, + 4, + 1, + 4, + 1, + 4, + 1, + 4, + 5, + 4, + 167, + 8, + 4, + 10, + 4, + 12, + 4, + 170, + 9, + 4, + 3, + 4, + 172, + 8, + 4, + 1, + 5, + 1, + 5, + 3, + 5, + 176, + 8, + 5, + 1, + 6, + 1, + 6, + 1, + 6, + 1, + 6, + 5, + 6, + 182, + 8, + 6, + 10, + 6, + 12, + 6, + 185, + 9, + 6, + 1, + 6, + 1, + 6, + 1, + 6, + 1, + 6, + 5, + 6, + 191, + 8, + 6, + 10, + 6, + 12, + 6, + 194, + 9, + 6, + 1, + 6, + 1, + 6, + 1, + 6, + 1, + 6, + 5, + 6, + 200, + 8, + 6, + 10, + 6, + 12, + 6, + 203, + 9, + 6, + 3, + 6, + 205, + 8, + 6, + 1, + 7, + 3, + 7, + 208, + 8, + 7, + 1, + 7, + 1, + 7, + 1, + 8, + 1, + 8, + 1, + 8, + 1, + 8, + 1, + 8, + 1, + 8, + 1, + 8, + 1, + 8, + 1, + 9, + 4, + 9, + 221, + 8, + 9, + 11, + 9, + 12, + 9, + 222, + 1, + 10, + 1, + 10, + 1, + 10, + 3, + 10, + 228, + 8, + 10, + 1, + 11, + 1, + 11, + 5, + 11, + 232, + 8, + 11, + 10, + 11, + 12, + 11, + 235, + 9, + 11, + 1, + 11, + 3, + 11, + 238, + 8, + 11, + 1, + 12, + 1, + 12, + 1, + 12, + 1, + 12, + 1, + 12, + 1, + 12, + 1, + 13, + 1, + 13, + 1, + 13, + 1, + 14, + 1, + 14, + 1, + 14, + 1, + 14, + 1, + 14, + 1, + 14, + 1, + 14, + 1, + 15, + 1, + 15, + 1, + 15, + 1, + 15, + 1, + 16, + 1, + 16, + 4, + 16, + 262, + 8, + 16, + 11, + 16, + 12, + 16, + 263, + 1, + 16, + 1, + 16, + 1, + 16, + 3, + 16, + 269, + 8, + 16, + 1, + 17, + 1, + 17, + 4, + 17, + 273, + 8, + 17, + 11, + 17, + 12, + 17, + 274, + 1, + 17, + 3, + 17, + 278, + 8, + 17, + 1, + 17, + 1, + 17, + 1, + 18, + 1, + 18, + 1, + 18, + 1, + 18, + 1, + 18, + 1, + 19, + 1, + 19, + 1, + 19, + 1, + 19, + 1, + 20, + 1, + 20, + 1, + 20, + 1, + 20, + 1, + 21, + 1, + 21, + 1, + 22, + 1, + 22, + 1, + 22, + 1, + 22, + 1, + 22, + 3, + 22, + 302, + 8, + 22, + 1, + 22, + 1, + 22, + 1, + 23, + 1, + 23, + 1, + 23, + 5, + 23, + 309, + 8, + 23, + 10, + 23, + 12, + 23, + 312, + 9, + 23, + 1, + 24, + 1, + 24, + 3, + 24, + 316, + 8, + 24, + 1, + 25, + 1, + 25, + 1, + 25, + 1, + 25, + 3, + 25, + 322, + 8, + 25, + 1, + 26, + 1, + 26, + 1, + 26, + 1, + 26, + 1, + 26, + 3, + 26, + 329, + 8, + 26, + 1, + 27, + 1, + 27, + 1, + 27, + 1, + 27, + 1, + 27, + 1, + 27, + 1, + 28, + 1, + 28, + 3, + 28, + 339, + 8, + 28, + 1, + 29, + 1, + 29, + 1, + 29, + 5, + 29, + 344, + 8, + 29, + 10, + 29, + 12, + 29, + 347, + 9, + 29, + 1, + 30, + 1, + 30, + 1, + 30, + 5, + 30, + 352, + 8, + 30, + 10, + 30, + 12, + 30, + 355, + 9, + 30, + 1, + 31, + 1, + 31, + 1, + 31, + 1, + 31, + 1, + 31, + 1, + 31, + 1, + 32, + 1, + 32, + 1, + 33, + 1, + 33, + 1, + 34, + 1, + 34, + 1, + 34, + 1, + 34, + 1, + 34, + 3, + 34, + 372, + 8, + 34, + 1, + 35, + 1, + 35, + 1, + 35, + 1, + 35, + 5, + 35, + 378, + 8, + 35, + 10, + 35, + 12, + 35, + 381, + 9, + 35, + 1, + 36, + 1, + 36, + 1, + 36, + 1, + 36, + 5, + 36, + 387, + 8, + 36, + 10, + 36, + 12, + 36, + 390, + 9, + 36, + 1, + 37, + 1, + 37, + 1, + 37, + 1, + 37, + 5, + 37, + 396, + 8, + 37, + 10, + 37, + 12, + 37, + 399, + 9, + 37, + 1, + 38, + 1, + 38, + 1, + 38, + 1, + 38, + 5, + 38, + 405, + 8, + 38, + 10, + 38, + 12, + 38, + 408, + 9, + 38, + 1, + 39, + 1, + 39, + 1, + 39, + 1, + 39, + 5, + 39, + 414, + 8, + 39, + 10, + 39, + 12, + 39, + 417, + 9, + 39, + 1, + 40, + 1, + 40, + 1, + 40, + 1, + 40, + 1, + 40, + 3, + 40, + 424, + 8, + 40, + 1, + 41, + 1, + 41, + 1, + 41, + 1, + 41, + 3, + 41, + 430, + 8, + 41, + 1, + 42, + 3, + 42, + 433, + 8, + 42, + 1, + 42, + 1, + 42, + 1, + 42, + 1, + 42, + 1, + 43, + 1, + 43, + 3, + 43, + 441, + 8, + 43, + 1, + 43, + 1, + 43, + 1, + 43, + 3, + 43, + 446, + 8, + 43, + 5, + 43, + 448, + 8, + 43, + 10, + 43, + 12, + 43, + 451, + 9, + 43, + 1, + 44, + 3, + 44, + 454, + 8, + 44, + 1, + 44, + 1, + 44, + 3, + 44, + 458, + 8, + 44, + 1, + 45, + 1, + 45, + 3, + 45, + 462, + 8, + 45, + 1, + 45, + 1, + 45, + 1, + 45, + 3, + 45, + 467, + 8, + 45, + 5, + 45, + 469, + 8, + 45, + 10, + 45, + 12, + 45, + 472, + 9, + 45, + 1, + 46, + 1, + 46, + 1, + 46, + 1, + 46, + 1, + 47, + 1, + 47, + 1, + 48, + 1, + 48, + 1, + 48, + 1, + 48, + 3, + 48, + 484, + 8, + 48, + 1, + 48, + 1, + 48, + 1, + 49, + 1, + 49, + 3, + 49, + 490, + 8, + 49, + 1, + 50, + 1, + 50, + 1, + 50, + 1, + 50, + 5, + 50, + 496, + 8, + 50, + 10, + 50, + 12, + 50, + 499, + 9, + 50, + 1, + 50, + 1, + 50, + 3, + 50, + 503, + 8, + 50, + 1, + 51, + 1, + 51, + 1, + 52, + 1, + 52, + 1, + 53, + 1, + 53, + 1, + 53, + 3, + 53, + 512, + 8, + 53, + 1, + 54, + 1, + 54, + 1, + 54, + 4, + 54, + 517, + 8, + 54, + 11, + 54, + 12, + 54, + 518, + 1, + 54, + 1, + 54, + 1, + 54, + 1, + 54, + 3, + 54, + 525, + 8, + 54, + 1, + 54, + 1, + 54, + 1, + 55, + 1, + 55, + 1, + 55, + 1, + 55, + 1, + 56, + 1, + 56, + 3, + 56, + 535, + 8, + 56, + 1, + 57, + 1, + 57, + 1, + 57, + 1, + 57, + 1, + 57, + 1, + 57, + 1, + 57, + 5, + 57, + 544, + 8, + 57, + 10, + 57, + 12, + 57, + 547, + 9, + 57, + 1, + 58, + 1, + 58, + 1, + 58, + 1, + 58, + 3, + 58, + 553, + 8, + 58, + 1, + 58, + 1, + 58, + 1, + 58, + 1, + 58, + 3, + 58, + 559, + 8, + 58, + 1, + 58, + 1, + 58, + 1, + 58, + 1, + 58, + 3, + 58, + 565, + 8, + 58, + 1, + 58, + 1, + 58, + 1, + 58, + 1, + 58, + 3, + 58, + 571, + 8, + 58, + 1, + 58, + 1, + 58, + 1, + 58, + 1, + 58, + 3, + 58, + 577, + 8, + 58, + 3, + 58, + 579, + 8, + 58, + 1, + 59, + 1, + 59, + 1, + 60, + 1, + 60, + 1, + 61, + 1, + 61, + 1, + 62, + 1, + 62, + 1, + 63, + 1, + 63, + 1, + 64, + 1, + 64, + 1, + 64, + 0, + 0, + 65, + 0, + 2, + 4, + 6, + 8, + 10, + 12, + 14, + 16, + 18, + 20, + 22, + 24, + 26, + 28, + 30, + 32, + 34, + 36, + 38, + 40, + 42, + 44, + 46, + 48, + 50, + 52, + 54, + 56, + 58, + 60, + 62, + 64, + 66, + 68, + 70, + 72, + 74, + 76, + 78, + 80, + 82, + 84, + 86, + 88, + 90, + 92, + 94, + 96, + 98, + 100, + 102, + 104, + 106, + 108, + 110, + 112, + 114, + 116, + 118, + 120, + 122, + 124, + 126, + 128, + 0, + 7, + 2, + 0, + 23, + 24, + 26, + 26, + 1, + 0, + 36, + 37, + 1, + 0, + 41, + 51, + 5, + 0, + 19, + 19, + 38, + 42, + 45, + 45, + 47, + 47, + 49, + 65, + 1, + 0, + 43, + 51, + 5, + 0, + 45, + 45, + 47, + 47, + 49, + 51, + 55, + 56, + 59, + 60, + 1, + 0, + 71, + 75, + 601, + 0, + 131, + 1, + 0, + 0, + 0, + 2, + 135, + 1, + 0, + 0, + 0, + 4, + 139, + 1, + 0, + 0, + 0, + 6, + 151, + 1, + 0, + 0, + 0, + 8, + 171, + 1, + 0, + 0, + 0, + 10, + 173, + 1, + 0, + 0, + 0, + 12, + 204, + 1, + 0, + 0, + 0, + 14, + 207, + 1, + 0, + 0, + 0, + 16, + 211, + 1, + 0, + 0, + 0, + 18, + 220, + 1, + 0, + 0, + 0, + 20, + 227, + 1, + 0, + 0, + 0, + 22, + 229, + 1, + 0, + 0, + 0, + 24, + 239, + 1, + 0, + 0, + 0, + 26, + 245, + 1, + 0, + 0, + 0, + 28, + 248, + 1, + 0, + 0, + 0, + 30, + 255, + 1, + 0, + 0, + 0, + 32, + 268, + 1, + 0, + 0, + 0, + 34, + 270, + 1, + 0, + 0, + 0, + 36, + 281, + 1, + 0, + 0, + 0, + 38, + 286, + 1, + 0, + 0, + 0, + 40, + 290, + 1, + 0, + 0, + 0, + 42, + 294, + 1, + 0, + 0, + 0, + 44, + 296, + 1, + 0, + 0, + 0, + 46, + 305, + 1, + 0, + 0, + 0, + 48, + 313, + 1, + 0, + 0, + 0, + 50, + 321, + 1, + 0, + 0, + 0, + 52, + 328, + 1, + 0, + 0, + 0, + 54, + 330, + 1, + 0, + 0, + 0, + 56, + 338, + 1, + 0, + 0, + 0, + 58, + 340, + 1, + 0, + 0, + 0, + 60, + 348, + 1, + 0, + 0, + 0, + 62, + 356, + 1, + 0, + 0, + 0, + 64, + 362, + 1, + 0, + 0, + 0, + 66, + 364, + 1, + 0, + 0, + 0, + 68, + 371, + 1, + 0, + 0, + 0, + 70, + 373, + 1, + 0, + 0, + 0, + 72, + 382, + 1, + 0, + 0, + 0, + 74, + 391, + 1, + 0, + 0, + 0, + 76, + 400, + 1, + 0, + 0, + 0, + 78, + 409, + 1, + 0, + 0, + 0, + 80, + 418, + 1, + 0, + 0, + 0, + 82, + 425, + 1, + 0, + 0, + 0, + 84, + 432, + 1, + 0, + 0, + 0, + 86, + 440, + 1, + 0, + 0, + 0, + 88, + 457, + 1, + 0, + 0, + 0, + 90, + 461, + 1, + 0, + 0, + 0, + 92, + 473, + 1, + 0, + 0, + 0, + 94, + 477, + 1, + 0, + 0, + 0, + 96, + 479, + 1, + 0, + 0, + 0, + 98, + 489, + 1, + 0, + 0, + 0, + 100, + 491, + 1, + 0, + 0, + 0, + 102, + 504, + 1, + 0, + 0, + 0, + 104, + 506, + 1, + 0, + 0, + 0, + 106, + 511, + 1, + 0, + 0, + 0, + 108, + 513, + 1, + 0, + 0, + 0, + 110, + 528, + 1, + 0, + 0, + 0, + 112, + 534, + 1, + 0, + 0, + 0, + 114, + 536, + 1, + 0, + 0, + 0, + 116, + 578, + 1, + 0, + 0, + 0, + 118, + 580, + 1, + 0, + 0, + 0, + 120, + 582, + 1, + 0, + 0, + 0, + 122, + 584, + 1, + 0, + 0, + 0, + 124, + 586, + 1, + 0, + 0, + 0, + 126, + 588, + 1, + 0, + 0, + 0, + 128, + 590, + 1, + 0, + 0, + 0, + 130, + 132, + 3, + 2, + 1, + 0, + 131, + 130, + 1, + 0, + 0, + 0, + 132, + 133, + 1, + 0, + 0, + 0, + 133, + 131, + 1, + 0, + 0, + 0, + 133, + 134, + 1, + 0, + 0, + 0, + 134, + 1, + 1, + 0, + 0, + 0, + 135, + 136, + 3, + 4, + 2, + 0, + 136, + 137, + 3, + 6, + 3, + 0, + 137, + 138, + 5, + 1, + 0, + 0, + 138, + 3, + 1, + 0, + 0, + 0, + 139, + 140, + 5, + 2, + 0, + 0, + 140, + 145, + 5, + 66, + 0, + 0, + 141, + 142, + 5, + 3, + 0, + 0, + 142, + 143, + 3, + 8, + 4, + 0, + 143, + 144, + 5, + 4, + 0, + 0, + 144, + 146, + 1, + 0, + 0, + 0, + 145, + 141, + 1, + 0, + 0, + 0, + 145, + 146, + 1, + 0, + 0, + 0, + 146, + 147, + 1, + 0, + 0, + 0, + 147, + 148, + 5, + 5, + 0, + 0, + 148, + 5, + 1, + 0, + 0, + 0, + 149, + 152, + 3, + 50, + 25, + 0, + 150, + 152, + 3, + 16, + 8, + 0, + 151, + 149, + 1, + 0, + 0, + 0, + 151, + 150, + 1, + 0, + 0, + 0, + 152, + 153, + 1, + 0, + 0, + 0, + 153, + 151, + 1, + 0, + 0, + 0, + 153, + 154, + 1, + 0, + 0, + 0, + 154, + 7, + 1, + 0, + 0, + 0, + 155, + 160, + 3, + 10, + 5, + 0, + 156, + 157, + 5, + 6, + 0, + 0, + 157, + 159, + 3, + 10, + 5, + 0, + 158, + 156, + 1, + 0, + 0, + 0, + 159, + 162, + 1, + 0, + 0, + 0, + 160, + 158, + 1, + 0, + 0, + 0, + 160, + 161, + 1, + 0, + 0, + 0, + 161, + 172, + 1, + 0, + 0, + 0, + 162, + 160, + 1, + 0, + 0, + 0, + 163, + 168, + 3, + 12, + 6, + 0, + 164, + 165, + 5, + 6, + 0, + 0, + 165, + 167, + 3, + 12, + 6, + 0, + 166, + 164, + 1, + 0, + 0, + 0, + 167, + 170, + 1, + 0, + 0, + 0, + 168, + 166, + 1, + 0, + 0, + 0, + 168, + 169, + 1, + 0, + 0, + 0, + 169, + 172, + 1, + 0, + 0, + 0, + 170, + 168, + 1, + 0, + 0, + 0, + 171, + 155, + 1, + 0, + 0, + 0, + 171, + 163, + 1, + 0, + 0, + 0, + 172, + 9, + 1, + 0, + 0, + 0, + 173, + 175, + 5, + 66, + 0, + 0, + 174, + 176, + 3, + 96, + 48, + 0, + 175, + 174, + 1, + 0, + 0, + 0, + 175, + 176, + 1, + 0, + 0, + 0, + 176, + 11, + 1, + 0, + 0, + 0, + 177, + 178, + 5, + 7, + 0, + 0, + 178, + 183, + 3, + 14, + 7, + 0, + 179, + 180, + 5, + 6, + 0, + 0, + 180, + 182, + 3, + 14, + 7, + 0, + 181, + 179, + 1, + 0, + 0, + 0, + 182, + 185, + 1, + 0, + 0, + 0, + 183, + 181, + 1, + 0, + 0, + 0, + 183, + 184, + 1, + 0, + 0, + 0, + 184, + 205, + 1, + 0, + 0, + 0, + 185, + 183, + 1, + 0, + 0, + 0, + 186, + 187, + 5, + 8, + 0, + 0, + 187, + 192, + 3, + 14, + 7, + 0, + 188, + 189, + 5, + 6, + 0, + 0, + 189, + 191, + 3, + 14, + 7, + 0, + 190, + 188, + 1, + 0, + 0, + 0, + 191, + 194, + 1, + 0, + 0, + 0, + 192, + 190, + 1, + 0, + 0, + 0, + 192, + 193, + 1, + 0, + 0, + 0, + 193, + 205, + 1, + 0, + 0, + 0, + 194, + 192, + 1, + 0, + 0, + 0, + 195, + 196, + 5, + 9, + 0, + 0, + 196, + 201, + 3, + 14, + 7, + 0, + 197, + 198, + 5, + 6, + 0, + 0, + 198, + 200, + 3, + 14, + 7, + 0, + 199, + 197, + 1, + 0, + 0, + 0, + 200, + 203, + 1, + 0, + 0, + 0, + 201, + 199, + 1, + 0, + 0, + 0, + 201, + 202, + 1, + 0, + 0, + 0, + 202, + 205, + 1, + 0, + 0, + 0, + 203, + 201, + 1, + 0, + 0, + 0, + 204, + 177, + 1, + 0, + 0, + 0, + 204, + 186, + 1, + 0, + 0, + 0, + 204, + 195, + 1, + 0, + 0, + 0, + 205, + 13, + 1, + 0, + 0, + 0, + 206, + 208, + 3, + 96, + 48, + 0, + 207, + 206, + 1, + 0, + 0, + 0, + 207, + 208, + 1, + 0, + 0, + 0, + 208, + 209, + 1, + 0, + 0, + 0, + 209, + 210, + 5, + 66, + 0, + 0, + 210, + 15, + 1, + 0, + 0, + 0, + 211, + 212, + 5, + 10, + 0, + 0, + 212, + 213, + 5, + 3, + 0, + 0, + 213, + 214, + 3, + 46, + 23, + 0, + 214, + 215, + 5, + 4, + 0, + 0, + 215, + 216, + 5, + 11, + 0, + 0, + 216, + 217, + 3, + 18, + 9, + 0, + 217, + 218, + 5, + 12, + 0, + 0, + 218, + 17, + 1, + 0, + 0, + 0, + 219, + 221, + 3, + 20, + 10, + 0, + 220, + 219, + 1, + 0, + 0, + 0, + 221, + 222, + 1, + 0, + 0, + 0, + 222, + 220, + 1, + 0, + 0, + 0, + 222, + 223, + 1, + 0, + 0, + 0, + 223, + 19, + 1, + 0, + 0, + 0, + 224, + 228, + 3, + 44, + 22, + 0, + 225, + 228, + 3, + 34, + 17, + 0, + 226, + 228, + 3, + 22, + 11, + 0, + 227, + 224, + 1, + 0, + 0, + 0, + 227, + 225, + 1, + 0, + 0, + 0, + 227, + 226, + 1, + 0, + 0, + 0, + 228, + 21, + 1, + 0, + 0, + 0, + 229, + 233, + 3, + 24, + 12, + 0, + 230, + 232, + 3, + 28, + 14, + 0, + 231, + 230, + 1, + 0, + 0, + 0, + 232, + 235, + 1, + 0, + 0, + 0, + 233, + 231, + 1, + 0, + 0, + 0, + 233, + 234, + 1, + 0, + 0, + 0, + 234, + 237, + 1, + 0, + 0, + 0, + 235, + 233, + 1, + 0, + 0, + 0, + 236, + 238, + 3, + 26, + 13, + 0, + 237, + 236, + 1, + 0, + 0, + 0, + 237, + 238, + 1, + 0, + 0, + 0, + 238, + 23, + 1, + 0, + 0, + 0, + 239, + 240, + 5, + 13, + 0, + 0, + 240, + 241, + 5, + 3, + 0, + 0, + 241, + 242, + 3, + 30, + 15, + 0, + 242, + 243, + 5, + 4, + 0, + 0, + 243, + 244, + 3, + 32, + 16, + 0, + 244, + 25, + 1, + 0, + 0, + 0, + 245, + 246, + 5, + 14, + 0, + 0, + 246, + 247, + 3, + 32, + 16, + 0, + 247, + 27, + 1, + 0, + 0, + 0, + 248, + 249, + 5, + 14, + 0, + 0, + 249, + 250, + 5, + 13, + 0, + 0, + 250, + 251, + 5, + 3, + 0, + 0, + 251, + 252, + 3, + 30, + 15, + 0, + 252, + 253, + 5, + 4, + 0, + 0, + 253, + 254, + 3, + 32, + 16, + 0, + 254, + 29, + 1, + 0, + 0, + 0, + 255, + 256, + 3, + 102, + 51, + 0, + 256, + 257, + 3, + 126, + 63, + 0, + 257, + 258, + 3, + 42, + 21, + 0, + 258, + 31, + 1, + 0, + 0, + 0, + 259, + 261, + 5, + 11, + 0, + 0, + 260, + 262, + 3, + 44, + 22, + 0, + 261, + 260, + 1, + 0, + 0, + 0, + 262, + 263, + 1, + 0, + 0, + 0, + 263, + 261, + 1, + 0, + 0, + 0, + 263, + 264, + 1, + 0, + 0, + 0, + 264, + 265, + 1, + 0, + 0, + 0, + 265, + 266, + 5, + 12, + 0, + 0, + 266, + 269, + 1, + 0, + 0, + 0, + 267, + 269, + 3, + 44, + 22, + 0, + 268, + 259, + 1, + 0, + 0, + 0, + 268, + 267, + 1, + 0, + 0, + 0, + 269, + 33, + 1, + 0, + 0, + 0, + 270, + 272, + 3, + 36, + 18, + 0, + 271, + 273, + 3, + 38, + 19, + 0, + 272, + 271, + 1, + 0, + 0, + 0, + 273, + 274, + 1, + 0, + 0, + 0, + 274, + 272, + 1, + 0, + 0, + 0, + 274, + 275, + 1, + 0, + 0, + 0, + 275, + 277, + 1, + 0, + 0, + 0, + 276, + 278, + 3, + 40, + 20, + 0, + 277, + 276, + 1, + 0, + 0, + 0, + 277, + 278, + 1, + 0, + 0, + 0, + 278, + 279, + 1, + 0, + 0, + 0, + 279, + 280, + 5, + 15, + 0, + 0, + 280, + 35, + 1, + 0, + 0, + 0, + 281, + 282, + 5, + 16, + 0, + 0, + 282, + 283, + 5, + 3, + 0, + 0, + 283, + 284, + 3, + 102, + 51, + 0, + 284, + 285, + 5, + 4, + 0, + 0, + 285, + 37, + 1, + 0, + 0, + 0, + 286, + 287, + 3, + 42, + 21, + 0, + 287, + 288, + 5, + 17, + 0, + 0, + 288, + 289, + 3, + 32, + 16, + 0, + 289, + 39, + 1, + 0, + 0, + 0, + 290, + 291, + 5, + 18, + 0, + 0, + 291, + 292, + 5, + 17, + 0, + 0, + 292, + 293, + 3, + 32, + 16, + 0, + 293, + 41, + 1, + 0, + 0, + 0, + 294, + 295, + 3, + 128, + 64, + 0, + 295, + 43, + 1, + 0, + 0, + 0, + 296, + 297, + 3, + 102, + 51, + 0, + 297, + 301, + 5, + 19, + 0, + 0, + 298, + 302, + 3, + 128, + 64, + 0, + 299, + 302, + 3, + 98, + 49, + 0, + 300, + 302, + 3, + 86, + 43, + 0, + 301, + 298, + 1, + 0, + 0, + 0, + 301, + 299, + 1, + 0, + 0, + 0, + 301, + 300, + 1, + 0, + 0, + 0, + 302, + 303, + 1, + 0, + 0, + 0, + 303, + 304, + 5, + 5, + 0, + 0, + 304, + 45, + 1, + 0, + 0, + 0, + 305, + 310, + 3, + 48, + 24, + 0, + 306, + 307, + 5, + 6, + 0, + 0, + 307, + 309, + 3, + 48, + 24, + 0, + 308, + 306, + 1, + 0, + 0, + 0, + 309, + 312, + 1, + 0, + 0, + 0, + 310, + 308, + 1, + 0, + 0, + 0, + 310, + 311, + 1, + 0, + 0, + 0, + 311, + 47, + 1, + 0, + 0, + 0, + 312, + 310, + 1, + 0, + 0, + 0, + 313, + 315, + 5, + 66, + 0, + 0, + 314, + 316, + 3, + 96, + 48, + 0, + 315, + 314, + 1, + 0, + 0, + 0, + 315, + 316, + 1, + 0, + 0, + 0, + 316, + 49, + 1, + 0, + 0, + 0, + 317, + 318, + 3, + 52, + 26, + 0, + 318, + 319, + 5, + 5, + 0, + 0, + 319, + 322, + 1, + 0, + 0, + 0, + 320, + 322, + 3, + 106, + 53, + 0, + 321, + 317, + 1, + 0, + 0, + 0, + 321, + 320, + 1, + 0, + 0, + 0, + 322, + 51, + 1, + 0, + 0, + 0, + 323, + 329, + 3, + 104, + 52, + 0, + 324, + 329, + 3, + 80, + 40, + 0, + 325, + 329, + 3, + 68, + 34, + 0, + 326, + 329, + 3, + 82, + 41, + 0, + 327, + 329, + 3, + 54, + 27, + 0, + 328, + 323, + 1, + 0, + 0, + 0, + 328, + 324, + 1, + 0, + 0, + 0, + 328, + 325, + 1, + 0, + 0, + 0, + 328, + 326, + 1, + 0, + 0, + 0, + 328, + 327, + 1, + 0, + 0, + 0, + 329, + 53, + 1, + 0, + 0, + 0, + 330, + 331, + 3, + 66, + 33, + 0, + 331, + 332, + 3, + 64, + 32, + 0, + 332, + 333, + 5, + 3, + 0, + 0, + 333, + 334, + 3, + 56, + 28, + 0, + 334, + 335, + 5, + 4, + 0, + 0, + 335, + 55, + 1, + 0, + 0, + 0, + 336, + 339, + 3, + 58, + 29, + 0, + 337, + 339, + 3, + 60, + 30, + 0, + 338, + 336, + 1, + 0, + 0, + 0, + 338, + 337, + 1, + 0, + 0, + 0, + 339, + 57, + 1, + 0, + 0, + 0, + 340, + 345, + 3, + 10, + 5, + 0, + 341, + 342, + 5, + 6, + 0, + 0, + 342, + 344, + 3, + 10, + 5, + 0, + 343, + 341, + 1, + 0, + 0, + 0, + 344, + 347, + 1, + 0, + 0, + 0, + 345, + 343, + 1, + 0, + 0, + 0, + 345, + 346, + 1, + 0, + 0, + 0, + 346, + 59, + 1, + 0, + 0, + 0, + 347, + 345, + 1, + 0, + 0, + 0, + 348, + 353, + 3, + 62, + 31, + 0, + 349, + 350, + 5, + 6, + 0, + 0, + 350, + 352, + 3, + 62, + 31, + 0, + 351, + 349, + 1, + 0, + 0, + 0, + 352, + 355, + 1, + 0, + 0, + 0, + 353, + 351, + 1, + 0, + 0, + 0, + 353, + 354, + 1, + 0, + 0, + 0, + 354, + 61, + 1, + 0, + 0, + 0, + 355, + 353, + 1, + 0, + 0, + 0, + 356, + 357, + 5, + 20, + 0, + 0, + 357, + 358, + 5, + 66, + 0, + 0, + 358, + 359, + 5, + 3, + 0, + 0, + 359, + 360, + 3, + 98, + 49, + 0, + 360, + 361, + 5, + 4, + 0, + 0, + 361, + 63, + 1, + 0, + 0, + 0, + 362, + 363, + 5, + 66, + 0, + 0, + 363, + 65, + 1, + 0, + 0, + 0, + 364, + 365, + 5, + 66, + 0, + 0, + 365, + 67, + 1, + 0, + 0, + 0, + 366, + 372, + 3, + 72, + 36, + 0, + 367, + 372, + 3, + 74, + 37, + 0, + 368, + 372, + 3, + 78, + 39, + 0, + 369, + 372, + 3, + 70, + 35, + 0, + 370, + 372, + 3, + 76, + 38, + 0, + 371, + 366, + 1, + 0, + 0, + 0, + 371, + 367, + 1, + 0, + 0, + 0, + 371, + 368, + 1, + 0, + 0, + 0, + 371, + 369, + 1, + 0, + 0, + 0, + 371, + 370, + 1, + 0, + 0, + 0, + 372, + 69, + 1, + 0, + 0, + 0, + 373, + 374, + 5, + 21, + 0, + 0, + 374, + 379, + 3, + 14, + 7, + 0, + 375, + 376, + 5, + 6, + 0, + 0, + 376, + 378, + 3, + 14, + 7, + 0, + 377, + 375, + 1, + 0, + 0, + 0, + 378, + 381, + 1, + 0, + 0, + 0, + 379, + 377, + 1, + 0, + 0, + 0, + 379, + 380, + 1, + 0, + 0, + 0, + 380, + 71, + 1, + 0, + 0, + 0, + 381, + 379, + 1, + 0, + 0, + 0, + 382, + 383, + 5, + 22, + 0, + 0, + 383, + 388, + 3, + 14, + 7, + 0, + 384, + 385, + 5, + 6, + 0, + 0, + 385, + 387, + 3, + 14, + 7, + 0, + 386, + 384, + 1, + 0, + 0, + 0, + 387, + 390, + 1, + 0, + 0, + 0, + 388, + 386, + 1, + 0, + 0, + 0, + 388, + 389, + 1, + 0, + 0, + 0, + 389, + 73, + 1, + 0, + 0, + 0, + 390, + 388, + 1, + 0, + 0, + 0, + 391, + 392, + 5, + 23, + 0, + 0, + 392, + 397, + 3, + 14, + 7, + 0, + 393, + 394, + 5, + 6, + 0, + 0, + 394, + 396, + 3, + 14, + 7, + 0, + 395, + 393, + 1, + 0, + 0, + 0, + 396, + 399, + 1, + 0, + 0, + 0, + 397, + 395, + 1, + 0, + 0, + 0, + 397, + 398, + 1, + 0, + 0, + 0, + 398, + 75, + 1, + 0, + 0, + 0, + 399, + 397, + 1, + 0, + 0, + 0, + 400, + 401, + 5, + 24, + 0, + 0, + 401, + 406, + 3, + 14, + 7, + 0, + 402, + 403, + 5, + 6, + 0, + 0, + 403, + 405, + 3, + 14, + 7, + 0, + 404, + 402, + 1, + 0, + 0, + 0, + 405, + 408, + 1, + 0, + 0, + 0, + 406, + 404, + 1, + 0, + 0, + 0, + 406, + 407, + 1, + 0, + 0, + 0, + 407, + 77, + 1, + 0, + 0, + 0, + 408, + 406, + 1, + 0, + 0, + 0, + 409, + 410, + 5, + 25, + 0, + 0, + 410, + 415, + 3, + 82, + 41, + 0, + 411, + 412, + 5, + 6, + 0, + 0, + 412, + 414, + 3, + 82, + 41, + 0, + 413, + 411, + 1, + 0, + 0, + 0, + 414, + 417, + 1, + 0, + 0, + 0, + 415, + 413, + 1, + 0, + 0, + 0, + 415, + 416, + 1, + 0, + 0, + 0, + 416, + 79, + 1, + 0, + 0, + 0, + 417, + 415, + 1, + 0, + 0, + 0, + 418, + 419, + 5, + 26, + 0, + 0, + 419, + 420, + 3, + 102, + 51, + 0, + 420, + 423, + 5, + 27, + 0, + 0, + 421, + 424, + 3, + 84, + 42, + 0, + 422, + 424, + 3, + 86, + 43, + 0, + 423, + 421, + 1, + 0, + 0, + 0, + 423, + 422, + 1, + 0, + 0, + 0, + 424, + 81, + 1, + 0, + 0, + 0, + 425, + 426, + 5, + 66, + 0, + 0, + 426, + 429, + 5, + 27, + 0, + 0, + 427, + 430, + 3, + 84, + 42, + 0, + 428, + 430, + 3, + 86, + 43, + 0, + 429, + 427, + 1, + 0, + 0, + 0, + 429, + 428, + 1, + 0, + 0, + 0, + 430, + 83, + 1, + 0, + 0, + 0, + 431, + 433, + 3, + 120, + 60, + 0, + 432, + 431, + 1, + 0, + 0, + 0, + 432, + 433, + 1, + 0, + 0, + 0, + 433, + 434, + 1, + 0, + 0, + 0, + 434, + 435, + 5, + 3, + 0, + 0, + 435, + 436, + 3, + 86, + 43, + 0, + 436, + 437, + 5, + 4, + 0, + 0, + 437, + 85, + 1, + 0, + 0, + 0, + 438, + 441, + 3, + 84, + 42, + 0, + 439, + 441, + 3, + 88, + 44, + 0, + 440, + 438, + 1, + 0, + 0, + 0, + 440, + 439, + 1, + 0, + 0, + 0, + 441, + 449, + 1, + 0, + 0, + 0, + 442, + 445, + 3, + 122, + 61, + 0, + 443, + 446, + 3, + 84, + 42, + 0, + 444, + 446, + 3, + 88, + 44, + 0, + 445, + 443, + 1, + 0, + 0, + 0, + 445, + 444, + 1, + 0, + 0, + 0, + 446, + 448, + 1, + 0, + 0, + 0, + 447, + 442, + 1, + 0, + 0, + 0, + 448, + 451, + 1, + 0, + 0, + 0, + 449, + 447, + 1, + 0, + 0, + 0, + 449, + 450, + 1, + 0, + 0, + 0, + 450, + 87, + 1, + 0, + 0, + 0, + 451, + 449, + 1, + 0, + 0, + 0, + 452, + 454, + 3, + 120, + 60, + 0, + 453, + 452, + 1, + 0, + 0, + 0, + 453, + 454, + 1, + 0, + 0, + 0, + 454, + 455, + 1, + 0, + 0, + 0, + 455, + 458, + 3, + 98, + 49, + 0, + 456, + 458, + 3, + 128, + 64, + 0, + 457, + 453, + 1, + 0, + 0, + 0, + 457, + 456, + 1, + 0, + 0, + 0, + 458, + 89, + 1, + 0, + 0, + 0, + 459, + 462, + 3, + 84, + 42, + 0, + 460, + 462, + 3, + 88, + 44, + 0, + 461, + 459, + 1, + 0, + 0, + 0, + 461, + 460, + 1, + 0, + 0, + 0, + 462, + 470, + 1, + 0, + 0, + 0, + 463, + 466, + 3, + 122, + 61, + 0, + 464, + 467, + 3, + 84, + 42, + 0, + 465, + 467, + 3, + 88, + 44, + 0, + 466, + 464, + 1, + 0, + 0, + 0, + 466, + 465, + 1, + 0, + 0, + 0, + 467, + 469, + 1, + 0, + 0, + 0, + 468, + 463, + 1, + 0, + 0, + 0, + 469, + 472, + 1, + 0, + 0, + 0, + 470, + 468, + 1, + 0, + 0, + 0, + 470, + 471, + 1, + 0, + 0, + 0, + 471, + 91, + 1, + 0, + 0, + 0, + 472, + 470, + 1, + 0, + 0, + 0, + 473, + 474, + 3, + 90, + 45, + 0, + 474, + 475, + 3, + 126, + 63, + 0, + 475, + 476, + 3, + 94, + 47, + 0, + 476, + 93, + 1, + 0, + 0, + 0, + 477, + 478, + 3, + 128, + 64, + 0, + 478, + 95, + 1, + 0, + 0, + 0, + 479, + 480, + 5, + 28, + 0, + 0, + 480, + 483, + 5, + 72, + 0, + 0, + 481, + 482, + 5, + 17, + 0, + 0, + 482, + 484, + 5, + 72, + 0, + 0, + 483, + 481, + 1, + 0, + 0, + 0, + 483, + 484, + 1, + 0, + 0, + 0, + 484, + 485, + 1, + 0, + 0, + 0, + 485, + 486, + 5, + 29, + 0, + 0, + 486, + 97, + 1, + 0, + 0, + 0, + 487, + 490, + 3, + 10, + 5, + 0, + 488, + 490, + 3, + 100, + 50, + 0, + 489, + 487, + 1, + 0, + 0, + 0, + 489, + 488, + 1, + 0, + 0, + 0, + 490, + 99, + 1, + 0, + 0, + 0, + 491, + 492, + 5, + 30, + 0, + 0, + 492, + 497, + 3, + 10, + 5, + 0, + 493, + 494, + 5, + 6, + 0, + 0, + 494, + 496, + 3, + 10, + 5, + 0, + 495, + 493, + 1, + 0, + 0, + 0, + 496, + 499, + 1, + 0, + 0, + 0, + 497, + 495, + 1, + 0, + 0, + 0, + 497, + 498, + 1, + 0, + 0, + 0, + 498, + 500, + 1, + 0, + 0, + 0, + 499, + 497, + 1, + 0, + 0, + 0, + 500, + 502, + 5, + 31, + 0, + 0, + 501, + 503, + 3, + 96, + 48, + 0, + 502, + 501, + 1, + 0, + 0, + 0, + 502, + 503, + 1, + 0, + 0, + 0, + 503, + 101, + 1, + 0, + 0, + 0, + 504, + 505, + 3, + 98, + 49, + 0, + 505, + 103, + 1, + 0, + 0, + 0, + 506, + 507, + 3, + 12, + 6, + 0, + 507, + 105, + 1, + 0, + 0, + 0, + 508, + 512, + 3, + 114, + 57, + 0, + 509, + 512, + 3, + 108, + 54, + 0, + 510, + 512, + 3, + 110, + 55, + 0, + 511, + 508, + 1, + 0, + 0, + 0, + 511, + 509, + 1, + 0, + 0, + 0, + 511, + 510, + 1, + 0, + 0, + 0, + 512, + 107, + 1, + 0, + 0, + 0, + 513, + 514, + 5, + 32, + 0, + 0, + 514, + 516, + 5, + 33, + 0, + 0, + 515, + 517, + 5, + 66, + 0, + 0, + 516, + 515, + 1, + 0, + 0, + 0, + 517, + 518, + 1, + 0, + 0, + 0, + 518, + 516, + 1, + 0, + 0, + 0, + 518, + 519, + 1, + 0, + 0, + 0, + 519, + 520, + 1, + 0, + 0, + 0, + 520, + 521, + 5, + 33, + 0, + 0, + 521, + 524, + 5, + 33, + 0, + 0, + 522, + 525, + 3, + 112, + 56, + 0, + 523, + 525, + 7, + 0, + 0, + 0, + 524, + 522, + 1, + 0, + 0, + 0, + 524, + 523, + 1, + 0, + 0, + 0, + 525, + 526, + 1, + 0, + 0, + 0, + 526, + 527, + 5, + 33, + 0, + 0, + 527, + 109, + 1, + 0, + 0, + 0, + 528, + 529, + 5, + 34, + 0, + 0, + 529, + 530, + 5, + 66, + 0, + 0, + 530, + 531, + 5, + 66, + 0, + 0, + 531, + 111, + 1, + 0, + 0, + 0, + 532, + 535, + 3, + 122, + 61, + 0, + 533, + 535, + 3, + 120, + 60, + 0, + 534, + 532, + 1, + 0, + 0, + 0, + 534, + 533, + 1, + 0, + 0, + 0, + 535, + 113, + 1, + 0, + 0, + 0, + 536, + 537, + 5, + 35, + 0, + 0, + 537, + 538, + 5, + 33, + 0, + 0, + 538, + 539, + 3, + 112, + 56, + 0, + 539, + 540, + 5, + 33, + 0, + 0, + 540, + 545, + 3, + 116, + 58, + 0, + 541, + 542, + 7, + 1, + 0, + 0, + 542, + 544, + 3, + 116, + 58, + 0, + 543, + 541, + 1, + 0, + 0, + 0, + 544, + 547, + 1, + 0, + 0, + 0, + 545, + 543, + 1, + 0, + 0, + 0, + 545, + 546, + 1, + 0, + 0, + 0, + 546, + 115, + 1, + 0, + 0, + 0, + 547, + 545, + 1, + 0, + 0, + 0, + 548, + 549, + 5, + 66, + 0, + 0, + 549, + 550, + 5, + 27, + 0, + 0, + 550, + 552, + 3, + 128, + 64, + 0, + 551, + 553, + 3, + 118, + 59, + 0, + 552, + 551, + 1, + 0, + 0, + 0, + 552, + 553, + 1, + 0, + 0, + 0, + 553, + 579, + 1, + 0, + 0, + 0, + 554, + 555, + 5, + 66, + 0, + 0, + 555, + 556, + 5, + 38, + 0, + 0, + 556, + 558, + 3, + 128, + 64, + 0, + 557, + 559, + 3, + 118, + 59, + 0, + 558, + 557, + 1, + 0, + 0, + 0, + 558, + 559, + 1, + 0, + 0, + 0, + 559, + 579, + 1, + 0, + 0, + 0, + 560, + 561, + 5, + 66, + 0, + 0, + 561, + 562, + 5, + 39, + 0, + 0, + 562, + 564, + 3, + 128, + 64, + 0, + 563, + 565, + 3, + 118, + 59, + 0, + 564, + 563, + 1, + 0, + 0, + 0, + 564, + 565, + 1, + 0, + 0, + 0, + 565, + 579, + 1, + 0, + 0, + 0, + 566, + 567, + 5, + 66, + 0, + 0, + 567, + 568, + 5, + 40, + 0, + 0, + 568, + 570, + 3, + 128, + 64, + 0, + 569, + 571, + 3, + 118, + 59, + 0, + 570, + 569, + 1, + 0, + 0, + 0, + 570, + 571, + 1, + 0, + 0, + 0, + 571, + 579, + 1, + 0, + 0, + 0, + 572, + 573, + 5, + 66, + 0, + 0, + 573, + 574, + 5, + 19, + 0, + 0, + 574, + 576, + 3, + 128, + 64, + 0, + 575, + 577, + 3, + 118, + 59, + 0, + 576, + 575, + 1, + 0, + 0, + 0, + 576, + 577, + 1, + 0, + 0, + 0, + 577, + 579, + 1, + 0, + 0, + 0, + 578, + 548, + 1, + 0, + 0, + 0, + 578, + 554, + 1, + 0, + 0, + 0, + 578, + 560, + 1, + 0, + 0, + 0, + 578, + 566, + 1, + 0, + 0, + 0, + 578, + 572, + 1, + 0, + 0, + 0, + 579, + 117, + 1, + 0, + 0, + 0, + 580, + 581, + 5, + 66, + 0, + 0, + 581, + 119, + 1, + 0, + 0, + 0, + 582, + 583, + 7, + 2, + 0, + 0, + 583, + 121, + 1, + 0, + 0, + 0, + 584, + 585, + 7, + 3, + 0, + 0, + 585, + 123, + 1, + 0, + 0, + 0, + 586, + 587, + 7, + 4, + 0, + 0, + 587, + 125, + 1, + 0, + 0, + 0, + 588, + 589, + 7, + 5, + 0, + 0, + 589, + 127, + 1, + 0, + 0, + 0, + 590, + 591, + 7, + 6, + 0, + 0, + 591, + 129, + 1, + 0, + 0, + 0, + 61, + 133, + 145, + 151, + 153, + 160, + 168, + 171, + 175, + 183, + 192, + 201, + 204, + 207, + 222, + 227, + 233, + 237, + 263, + 268, + 274, + 277, + 301, + 310, + 315, + 321, + 328, + 338, + 345, + 353, + 371, + 379, + 388, + 397, + 406, + 415, + 423, + 429, + 432, + 440, + 445, + 449, + 453, + 457, + 461, + 466, + 470, + 483, + 489, + 497, + 502, + 511, + 518, + 524, + 534, + 545, + 552, + 558, + 564, + 570, + 576, + 578, + ] + + +class lfrXParser(Parser): grammarFileName = "lfrX.g4" atn = ATNDeserializer().deserialize(serializedATN()) - decisionsToDFA = [ DFA(ds, i) for i, ds in enumerate(atn.decisionToState) ] + decisionsToDFA = [DFA(ds, i) for i, ds in enumerate(atn.decisionToState)] sharedContextCache = PredictionContextCache() - literalNames = [ "", "'endmodule'", "'module'", "'('", "')'", - "';'", "','", "'finput'", "'foutput'", "'control'", - "'distribute@'", "'begin'", "'end'", "'if'", "'else'", - "'endcase'", "'case'", "':'", "'default'", "'<='", - "'.'", "'signal'", "'flow'", "'storage'", "'pump'", - "'number'", "'assign'", "'='", "'['", "']'", "'{'", - "'}'", "'#MAP'", "'\"'", "'#MATERIAL'", "'#CONSTRAIN'", - "'AND'", "'OR'", "'>'", "'<'", "'>='", "'+'", "'-'", - "'!'", "'~'", "'&'", "'~&'", "'|'", "'~|'", "'^'", - "'~^'", "'^~'", "'*'", "'/'", "'%'", "'=='", "'!='", - "'==='", "'!=='", "'&&'", "'||'", "'**'", "'>>'", "'<<'", - "'>>>'", "'<<<'" ] - - symbolicNames = [ "", "", "", "", - "", "", "", "", - "", "", "", "", - "", "", "", "", - "", "", "", "", - "", "", "", "", - "", "", "", "", - "", "", "", "", - "", "", "", "", - "", "", "", "", - "", "", "", "", - "", "", "", "", - "", "", "", "", - "", "", "", "", - "", "", "", "", - "", "", "", "", - "", "", "ID", "WS", "One_line_comment", - "Block_comment", "Import_line", "Real_number", "Decimal_number", - "Binary_number", "Octal_number", "Hex_number" ] + literalNames = [ + "", + "'endmodule'", + "'module'", + "'('", + "')'", + "';'", + "','", + "'finput'", + "'foutput'", + "'control'", + "'distribute@'", + "'begin'", + "'end'", + "'if'", + "'else'", + "'endcase'", + "'case'", + "':'", + "'default'", + "'<='", + "'.'", + "'signal'", + "'flow'", + "'storage'", + "'pump'", + "'number'", + "'assign'", + "'='", + "'['", + "']'", + "'{'", + "'}'", + "'#MAP'", + "'\"'", + "'#MATERIAL'", + "'#CONSTRAIN'", + "'AND'", + "'OR'", + "'>'", + "'<'", + "'>='", + "'+'", + "'-'", + "'!'", + "'~'", + "'&'", + "'~&'", + "'|'", + "'~|'", + "'^'", + "'~^'", + "'^~'", + "'*'", + "'/'", + "'%'", + "'=='", + "'!='", + "'==='", + "'!=='", + "'&&'", + "'||'", + "'**'", + "'>>'", + "'<<'", + "'>>>'", + "'<<<'", + ] + + symbolicNames = [ + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "ID", + "WS", + "One_line_comment", + "Block_comment", + "Import_line", + "Real_number", + "Decimal_number", + "Binary_number", + "Octal_number", + "Hex_number", + ] RULE_skeleton = 0 RULE_module = 1 @@ -398,163 +5298,207 @@ class lfrXParser ( Parser ): RULE_binary_module_path_operator = 63 RULE_number = 64 - ruleNames = [ "skeleton", "module", "moduledefinition", "body", "ioblock", - "vectorvar", "explicitIOBlock", "declvar", "distributionBlock", - "distributionBody", "distributeBodyStat", "ifElseBlock", - "ifBlock", "elseBlock", "elseIfBlock", "distributeCondition", - "statementBlock", "caseBlock", "caseBlockHeader", "casestat", - "defaultCaseStat", "distvalue", "distributionassignstat", - "sensitivitylist", "signal", "statements", "statement", - "moduleinstantiationstat", "instanceioblock", "orderedioblock", - "unorderedioblock", "explicitinstanceiomapping", "instancename", - "moduletype", "tempvariablesstat", "signalvarstat", "fluiddeclstat", - "storagestat", "pumpvarstat", "numvarstat", "assignstat", - "literalassignstat", "bracketexpression", "expression", - "expressionterm", "logiccondition_operand", "logiccondition", - "logic_value", "vector", "variables", "concatenation", - "lhs", "ioassignstat", "technologydirectives", "technologymappingdirective", - "materialmappingdirective", "mappingoperator", "performancedirective", - "constraint", "unit", "unary_operator", "binary_operator", - "unary_module_path_operator", "binary_module_path_operator", - "number" ] + ruleNames = [ + "skeleton", + "module", + "moduledefinition", + "body", + "ioblock", + "vectorvar", + "explicitIOBlock", + "declvar", + "distributionBlock", + "distributionBody", + "distributeBodyStat", + "ifElseBlock", + "ifBlock", + "elseBlock", + "elseIfBlock", + "distributeCondition", + "statementBlock", + "caseBlock", + "caseBlockHeader", + "casestat", + "defaultCaseStat", + "distvalue", + "distributionassignstat", + "sensitivitylist", + "signal", + "statements", + "statement", + "moduleinstantiationstat", + "instanceioblock", + "orderedioblock", + "unorderedioblock", + "explicitinstanceiomapping", + "instancename", + "moduletype", + "tempvariablesstat", + "signalvarstat", + "fluiddeclstat", + "storagestat", + "pumpvarstat", + "numvarstat", + "assignstat", + "literalassignstat", + "bracketexpression", + "expression", + "expressionterm", + "logiccondition_operand", + "logiccondition", + "logic_value", + "vector", + "variables", + "concatenation", + "lhs", + "ioassignstat", + "technologydirectives", + "technologymappingdirective", + "materialmappingdirective", + "mappingoperator", + "performancedirective", + "constraint", + "unit", + "unary_operator", + "binary_operator", + "unary_module_path_operator", + "binary_module_path_operator", + "number", + ] EOF = Token.EOF - T__0=1 - T__1=2 - T__2=3 - T__3=4 - T__4=5 - T__5=6 - T__6=7 - T__7=8 - T__8=9 - T__9=10 - T__10=11 - T__11=12 - T__12=13 - T__13=14 - T__14=15 - T__15=16 - T__16=17 - T__17=18 - T__18=19 - T__19=20 - T__20=21 - T__21=22 - T__22=23 - T__23=24 - T__24=25 - T__25=26 - T__26=27 - T__27=28 - T__28=29 - T__29=30 - T__30=31 - T__31=32 - T__32=33 - T__33=34 - T__34=35 - T__35=36 - T__36=37 - T__37=38 - T__38=39 - T__39=40 - T__40=41 - T__41=42 - T__42=43 - T__43=44 - T__44=45 - T__45=46 - T__46=47 - T__47=48 - T__48=49 - T__49=50 - T__50=51 - T__51=52 - T__52=53 - T__53=54 - T__54=55 - T__55=56 - T__56=57 - T__57=58 - T__58=59 - T__59=60 - T__60=61 - T__61=62 - T__62=63 - T__63=64 - T__64=65 - ID=66 - WS=67 - One_line_comment=68 - Block_comment=69 - Import_line=70 - Real_number=71 - Decimal_number=72 - Binary_number=73 - Octal_number=74 - Hex_number=75 - - def __init__(self, input:TokenStream, output:TextIO = sys.stdout): + T__0 = 1 + T__1 = 2 + T__2 = 3 + T__3 = 4 + T__4 = 5 + T__5 = 6 + T__6 = 7 + T__7 = 8 + T__8 = 9 + T__9 = 10 + T__10 = 11 + T__11 = 12 + T__12 = 13 + T__13 = 14 + T__14 = 15 + T__15 = 16 + T__16 = 17 + T__17 = 18 + T__18 = 19 + T__19 = 20 + T__20 = 21 + T__21 = 22 + T__22 = 23 + T__23 = 24 + T__24 = 25 + T__25 = 26 + T__26 = 27 + T__27 = 28 + T__28 = 29 + T__29 = 30 + T__30 = 31 + T__31 = 32 + T__32 = 33 + T__33 = 34 + T__34 = 35 + T__35 = 36 + T__36 = 37 + T__37 = 38 + T__38 = 39 + T__39 = 40 + T__40 = 41 + T__41 = 42 + T__42 = 43 + T__43 = 44 + T__44 = 45 + T__45 = 46 + T__46 = 47 + T__47 = 48 + T__48 = 49 + T__49 = 50 + T__50 = 51 + T__51 = 52 + T__52 = 53 + T__53 = 54 + T__54 = 55 + T__55 = 56 + T__56 = 57 + T__57 = 58 + T__58 = 59 + T__59 = 60 + T__60 = 61 + T__61 = 62 + T__62 = 63 + T__63 = 64 + T__64 = 65 + ID = 66 + WS = 67 + One_line_comment = 68 + Block_comment = 69 + Import_line = 70 + Real_number = 71 + Decimal_number = 72 + Binary_number = 73 + Octal_number = 74 + Hex_number = 75 + + def __init__(self, input: TokenStream, output: TextIO = sys.stdout): super().__init__(input, output) - self.checkVersion("4.9.1") - self._interp = ParserATNSimulator(self, self.atn, self.decisionsToDFA, self.sharedContextCache) + self.checkVersion("4.10.1") + self._interp = ParserATNSimulator( + self, self.atn, self.decisionsToDFA, self.sharedContextCache + ) self._predicates = None - - - class SkeletonContext(ParserRuleContext): - __slots__ = 'parser' + __slots__ = "parser" - def __init__(self, parser, parent:ParserRuleContext=None, invokingState:int=-1): + def __init__( + self, parser, parent: ParserRuleContext = None, invokingState: int = -1 + ): super().__init__(parent, invokingState) self.parser = parser - def module(self, i:int=None): + def module(self, i: int = None): if i is None: return self.getTypedRuleContexts(lfrXParser.ModuleContext) else: - return self.getTypedRuleContext(lfrXParser.ModuleContext,i) - + return self.getTypedRuleContext(lfrXParser.ModuleContext, i) def getRuleIndex(self): return lfrXParser.RULE_skeleton - def enterRule(self, listener:ParseTreeListener): - if hasattr( listener, "enterSkeleton" ): + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterSkeleton"): listener.enterSkeleton(self) - def exitRule(self, listener:ParseTreeListener): - if hasattr( listener, "exitSkeleton" ): + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitSkeleton"): listener.exitSkeleton(self) - def accept(self, visitor:ParseTreeVisitor): - if hasattr( visitor, "visitSkeleton" ): + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitSkeleton"): return visitor.visitSkeleton(self) else: return visitor.visitChildren(self) - - - def skeleton(self): - localctx = lfrXParser.SkeletonContext(self, self._ctx, self.state) self.enterRule(localctx, 0, self.RULE_skeleton) - self._la = 0 # Token type + self._la = 0 # Token type try: self.enterOuterAlt(localctx, 1) - self.state = 131 + self.state = 131 self._errHandler.sync(self) _la = self._input.LA(1) while True: self.state = 130 self.module() - self.state = 133 + self.state = 133 self._errHandler.sync(self) _la = self._input.LA(1) - if not (_la==lfrXParser.T__1): + if not (_la == lfrXParser.T__1): break except RecognitionException as re: @@ -565,44 +5509,39 @@ def skeleton(self): self.exitRule() return localctx - class ModuleContext(ParserRuleContext): - __slots__ = 'parser' + __slots__ = "parser" - def __init__(self, parser, parent:ParserRuleContext=None, invokingState:int=-1): + def __init__( + self, parser, parent: ParserRuleContext = None, invokingState: int = -1 + ): super().__init__(parent, invokingState) self.parser = parser def moduledefinition(self): - return self.getTypedRuleContext(lfrXParser.ModuledefinitionContext,0) - + return self.getTypedRuleContext(lfrXParser.ModuledefinitionContext, 0) def body(self): - return self.getTypedRuleContext(lfrXParser.BodyContext,0) - + return self.getTypedRuleContext(lfrXParser.BodyContext, 0) def getRuleIndex(self): return lfrXParser.RULE_module - def enterRule(self, listener:ParseTreeListener): - if hasattr( listener, "enterModule" ): + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterModule"): listener.enterModule(self) - def exitRule(self, listener:ParseTreeListener): - if hasattr( listener, "exitModule" ): + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitModule"): listener.exitModule(self) - def accept(self, visitor:ParseTreeVisitor): - if hasattr( visitor, "visitModule" ): + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitModule"): return visitor.visitModule(self) else: return visitor.visitChildren(self) - - - def module(self): - localctx = lfrXParser.ModuleContext(self, self._ctx, self.state) self.enterRule(localctx, 2, self.RULE_module) try: @@ -621,11 +5560,12 @@ def module(self): self.exitRule() return localctx - class ModuledefinitionContext(ParserRuleContext): - __slots__ = 'parser' + __slots__ = "parser" - def __init__(self, parser, parent:ParserRuleContext=None, invokingState:int=-1): + def __init__( + self, parser, parent: ParserRuleContext = None, invokingState: int = -1 + ): super().__init__(parent, invokingState) self.parser = parser @@ -633,34 +5573,29 @@ def ID(self): return self.getToken(lfrXParser.ID, 0) def ioblock(self): - return self.getTypedRuleContext(lfrXParser.IoblockContext,0) - + return self.getTypedRuleContext(lfrXParser.IoblockContext, 0) def getRuleIndex(self): return lfrXParser.RULE_moduledefinition - def enterRule(self, listener:ParseTreeListener): - if hasattr( listener, "enterModuledefinition" ): + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterModuledefinition"): listener.enterModuledefinition(self) - def exitRule(self, listener:ParseTreeListener): - if hasattr( listener, "exitModuledefinition" ): + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitModuledefinition"): listener.exitModuledefinition(self) - def accept(self, visitor:ParseTreeVisitor): - if hasattr( visitor, "visitModuledefinition" ): + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitModuledefinition"): return visitor.visitModuledefinition(self) else: return visitor.visitChildren(self) - - - def moduledefinition(self): - localctx = lfrXParser.ModuledefinitionContext(self, self._ctx, self.state) self.enterRule(localctx, 4, self.RULE_moduledefinition) - self._la = 0 # Token type + self._la = 0 # Token type try: self.enterOuterAlt(localctx, 1) self.state = 139 @@ -670,7 +5605,7 @@ def moduledefinition(self): self.state = 145 self._errHandler.sync(self) _la = self._input.LA(1) - if _la==lfrXParser.T__2: + if _la == lfrXParser.T__2: self.state = 141 self.match(lfrXParser.T__2) self.state = 142 @@ -678,7 +5613,6 @@ def moduledefinition(self): self.state = 143 self.match(lfrXParser.T__3) - self.state = 147 self.match(lfrXParser.T__4) except RecognitionException as re: @@ -689,63 +5623,72 @@ def moduledefinition(self): self.exitRule() return localctx - class BodyContext(ParserRuleContext): - __slots__ = 'parser' + __slots__ = "parser" - def __init__(self, parser, parent:ParserRuleContext=None, invokingState:int=-1): + def __init__( + self, parser, parent: ParserRuleContext = None, invokingState: int = -1 + ): super().__init__(parent, invokingState) self.parser = parser - def statements(self, i:int=None): + def statements(self, i: int = None): if i is None: return self.getTypedRuleContexts(lfrXParser.StatementsContext) else: - return self.getTypedRuleContext(lfrXParser.StatementsContext,i) + return self.getTypedRuleContext(lfrXParser.StatementsContext, i) - - def distributionBlock(self, i:int=None): + def distributionBlock(self, i: int = None): if i is None: return self.getTypedRuleContexts(lfrXParser.DistributionBlockContext) else: - return self.getTypedRuleContext(lfrXParser.DistributionBlockContext,i) - + return self.getTypedRuleContext(lfrXParser.DistributionBlockContext, i) def getRuleIndex(self): return lfrXParser.RULE_body - def enterRule(self, listener:ParseTreeListener): - if hasattr( listener, "enterBody" ): + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterBody"): listener.enterBody(self) - def exitRule(self, listener:ParseTreeListener): - if hasattr( listener, "exitBody" ): + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitBody"): listener.exitBody(self) - def accept(self, visitor:ParseTreeVisitor): - if hasattr( visitor, "visitBody" ): + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitBody"): return visitor.visitBody(self) else: return visitor.visitChildren(self) - - - def body(self): - localctx = lfrXParser.BodyContext(self, self._ctx, self.state) self.enterRule(localctx, 6, self.RULE_body) - self._la = 0 # Token type + self._la = 0 # Token type try: self.enterOuterAlt(localctx, 1) - self.state = 151 + self.state = 151 self._errHandler.sync(self) _la = self._input.LA(1) while True: self.state = 151 self._errHandler.sync(self) token = self._input.LA(1) - if token in [lfrXParser.T__6, lfrXParser.T__7, lfrXParser.T__8, lfrXParser.T__20, lfrXParser.T__21, lfrXParser.T__22, lfrXParser.T__23, lfrXParser.T__24, lfrXParser.T__25, lfrXParser.T__31, lfrXParser.T__33, lfrXParser.T__34, lfrXParser.ID]: + if token in [ + lfrXParser.T__6, + lfrXParser.T__7, + lfrXParser.T__8, + lfrXParser.T__20, + lfrXParser.T__21, + lfrXParser.T__22, + lfrXParser.T__23, + lfrXParser.T__24, + lfrXParser.T__25, + lfrXParser.T__31, + lfrXParser.T__33, + lfrXParser.T__34, + lfrXParser.ID, + ]: self.state = 149 self.statements() pass @@ -756,10 +5699,34 @@ def body(self): else: raise NoViableAltException(self) - self.state = 153 + self.state = 153 self._errHandler.sync(self) _la = self._input.LA(1) - if not (((((_la - 7)) & ~0x3f) == 0 and ((1 << (_la - 7)) & ((1 << (lfrXParser.T__6 - 7)) | (1 << (lfrXParser.T__7 - 7)) | (1 << (lfrXParser.T__8 - 7)) | (1 << (lfrXParser.T__9 - 7)) | (1 << (lfrXParser.T__20 - 7)) | (1 << (lfrXParser.T__21 - 7)) | (1 << (lfrXParser.T__22 - 7)) | (1 << (lfrXParser.T__23 - 7)) | (1 << (lfrXParser.T__24 - 7)) | (1 << (lfrXParser.T__25 - 7)) | (1 << (lfrXParser.T__31 - 7)) | (1 << (lfrXParser.T__33 - 7)) | (1 << (lfrXParser.T__34 - 7)) | (1 << (lfrXParser.ID - 7)))) != 0)): + if not ( + ( + (((_la - 7)) & ~0x3F) == 0 + and ( + (1 << (_la - 7)) + & ( + (1 << (lfrXParser.T__6 - 7)) + | (1 << (lfrXParser.T__7 - 7)) + | (1 << (lfrXParser.T__8 - 7)) + | (1 << (lfrXParser.T__9 - 7)) + | (1 << (lfrXParser.T__20 - 7)) + | (1 << (lfrXParser.T__21 - 7)) + | (1 << (lfrXParser.T__22 - 7)) + | (1 << (lfrXParser.T__23 - 7)) + | (1 << (lfrXParser.T__24 - 7)) + | (1 << (lfrXParser.T__25 - 7)) + | (1 << (lfrXParser.T__31 - 7)) + | (1 << (lfrXParser.T__33 - 7)) + | (1 << (lfrXParser.T__34 - 7)) + | (1 << (lfrXParser.ID - 7)) + ) + ) + != 0 + ) + ): break except RecognitionException as re: @@ -770,53 +5737,48 @@ def body(self): self.exitRule() return localctx - class IoblockContext(ParserRuleContext): - __slots__ = 'parser' + __slots__ = "parser" - def __init__(self, parser, parent:ParserRuleContext=None, invokingState:int=-1): + def __init__( + self, parser, parent: ParserRuleContext = None, invokingState: int = -1 + ): super().__init__(parent, invokingState) self.parser = parser - def vectorvar(self, i:int=None): + def vectorvar(self, i: int = None): if i is None: return self.getTypedRuleContexts(lfrXParser.VectorvarContext) else: - return self.getTypedRuleContext(lfrXParser.VectorvarContext,i) - + return self.getTypedRuleContext(lfrXParser.VectorvarContext, i) - def explicitIOBlock(self, i:int=None): + def explicitIOBlock(self, i: int = None): if i is None: return self.getTypedRuleContexts(lfrXParser.ExplicitIOBlockContext) else: - return self.getTypedRuleContext(lfrXParser.ExplicitIOBlockContext,i) - + return self.getTypedRuleContext(lfrXParser.ExplicitIOBlockContext, i) def getRuleIndex(self): return lfrXParser.RULE_ioblock - def enterRule(self, listener:ParseTreeListener): - if hasattr( listener, "enterIoblock" ): + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterIoblock"): listener.enterIoblock(self) - def exitRule(self, listener:ParseTreeListener): - if hasattr( listener, "exitIoblock" ): + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitIoblock"): listener.exitIoblock(self) - def accept(self, visitor:ParseTreeVisitor): - if hasattr( visitor, "visitIoblock" ): + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitIoblock"): return visitor.visitIoblock(self) else: return visitor.visitChildren(self) - - - def ioblock(self): - localctx = lfrXParser.IoblockContext(self, self._ctx, self.state) self.enterRule(localctx, 8, self.RULE_ioblock) - self._la = 0 # Token type + self._la = 0 # Token type try: self.state = 171 self._errHandler.sync(self) @@ -828,7 +5790,7 @@ def ioblock(self): self.state = 160 self._errHandler.sync(self) _la = self._input.LA(1) - while _la==lfrXParser.T__5: + while _la == lfrXParser.T__5: self.state = 156 self.match(lfrXParser.T__5) self.state = 157 @@ -845,7 +5807,7 @@ def ioblock(self): self.state = 168 self._errHandler.sync(self) _la = self._input.LA(1) - while _la==lfrXParser.T__5: + while _la == lfrXParser.T__5: self.state = 164 self.match(lfrXParser.T__5) self.state = 165 @@ -866,11 +5828,12 @@ def ioblock(self): self.exitRule() return localctx - class VectorvarContext(ParserRuleContext): - __slots__ = 'parser' + __slots__ = "parser" - def __init__(self, parser, parent:ParserRuleContext=None, invokingState:int=-1): + def __init__( + self, parser, parent: ParserRuleContext = None, invokingState: int = -1 + ): super().__init__(parent, invokingState) self.parser = parser @@ -878,34 +5841,29 @@ def ID(self): return self.getToken(lfrXParser.ID, 0) def vector(self): - return self.getTypedRuleContext(lfrXParser.VectorContext,0) - + return self.getTypedRuleContext(lfrXParser.VectorContext, 0) def getRuleIndex(self): return lfrXParser.RULE_vectorvar - def enterRule(self, listener:ParseTreeListener): - if hasattr( listener, "enterVectorvar" ): + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterVectorvar"): listener.enterVectorvar(self) - def exitRule(self, listener:ParseTreeListener): - if hasattr( listener, "exitVectorvar" ): + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitVectorvar"): listener.exitVectorvar(self) - def accept(self, visitor:ParseTreeVisitor): - if hasattr( visitor, "visitVectorvar" ): + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitVectorvar"): return visitor.visitVectorvar(self) else: return visitor.visitChildren(self) - - - def vectorvar(self): - localctx = lfrXParser.VectorvarContext(self, self._ctx, self.state) self.enterRule(localctx, 10, self.RULE_vectorvar) - self._la = 0 # Token type + self._la = 0 # Token type try: self.enterOuterAlt(localctx, 1) self.state = 173 @@ -913,11 +5871,10 @@ def vectorvar(self): self.state = 175 self._errHandler.sync(self) _la = self._input.LA(1) - if _la==lfrXParser.T__27: + if _la == lfrXParser.T__27: self.state = 174 self.vector() - except RecognitionException as re: localctx.exception = re self._errHandler.reportError(self, re) @@ -926,43 +5883,39 @@ def vectorvar(self): self.exitRule() return localctx - class ExplicitIOBlockContext(ParserRuleContext): - __slots__ = 'parser' + __slots__ = "parser" - def __init__(self, parser, parent:ParserRuleContext=None, invokingState:int=-1): + def __init__( + self, parser, parent: ParserRuleContext = None, invokingState: int = -1 + ): super().__init__(parent, invokingState) self.parser = parser - def declvar(self, i:int=None): + def declvar(self, i: int = None): if i is None: return self.getTypedRuleContexts(lfrXParser.DeclvarContext) else: - return self.getTypedRuleContext(lfrXParser.DeclvarContext,i) - + return self.getTypedRuleContext(lfrXParser.DeclvarContext, i) def getRuleIndex(self): return lfrXParser.RULE_explicitIOBlock - def enterRule(self, listener:ParseTreeListener): - if hasattr( listener, "enterExplicitIOBlock" ): + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterExplicitIOBlock"): listener.enterExplicitIOBlock(self) - def exitRule(self, listener:ParseTreeListener): - if hasattr( listener, "exitExplicitIOBlock" ): + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitExplicitIOBlock"): listener.exitExplicitIOBlock(self) - def accept(self, visitor:ParseTreeVisitor): - if hasattr( visitor, "visitExplicitIOBlock" ): + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitExplicitIOBlock"): return visitor.visitExplicitIOBlock(self) else: return visitor.visitChildren(self) - - - def explicitIOBlock(self): - localctx = lfrXParser.ExplicitIOBlockContext(self, self._ctx, self.state) self.enterRule(localctx, 12, self.RULE_explicitIOBlock) try: @@ -977,16 +5930,16 @@ def explicitIOBlock(self): self.declvar() self.state = 183 self._errHandler.sync(self) - _alt = self._interp.adaptivePredict(self._input,8,self._ctx) - while _alt!=2 and _alt!=ATN.INVALID_ALT_NUMBER: - if _alt==1: + _alt = self._interp.adaptivePredict(self._input, 8, self._ctx) + while _alt != 2 and _alt != ATN.INVALID_ALT_NUMBER: + if _alt == 1: self.state = 179 self.match(lfrXParser.T__5) self.state = 180 - self.declvar() + self.declvar() self.state = 185 self._errHandler.sync(self) - _alt = self._interp.adaptivePredict(self._input,8,self._ctx) + _alt = self._interp.adaptivePredict(self._input, 8, self._ctx) pass elif token in [lfrXParser.T__7]: @@ -997,16 +5950,16 @@ def explicitIOBlock(self): self.declvar() self.state = 192 self._errHandler.sync(self) - _alt = self._interp.adaptivePredict(self._input,9,self._ctx) - while _alt!=2 and _alt!=ATN.INVALID_ALT_NUMBER: - if _alt==1: + _alt = self._interp.adaptivePredict(self._input, 9, self._ctx) + while _alt != 2 and _alt != ATN.INVALID_ALT_NUMBER: + if _alt == 1: self.state = 188 self.match(lfrXParser.T__5) self.state = 189 - self.declvar() + self.declvar() self.state = 194 self._errHandler.sync(self) - _alt = self._interp.adaptivePredict(self._input,9,self._ctx) + _alt = self._interp.adaptivePredict(self._input, 9, self._ctx) pass elif token in [lfrXParser.T__8]: @@ -1017,16 +5970,16 @@ def explicitIOBlock(self): self.declvar() self.state = 201 self._errHandler.sync(self) - _alt = self._interp.adaptivePredict(self._input,10,self._ctx) - while _alt!=2 and _alt!=ATN.INVALID_ALT_NUMBER: - if _alt==1: + _alt = self._interp.adaptivePredict(self._input, 10, self._ctx) + while _alt != 2 and _alt != ATN.INVALID_ALT_NUMBER: + if _alt == 1: self.state = 197 self.match(lfrXParser.T__5) self.state = 198 - self.declvar() + self.declvar() self.state = 203 self._errHandler.sync(self) - _alt = self._interp.adaptivePredict(self._input,10,self._ctx) + _alt = self._interp.adaptivePredict(self._input, 10, self._ctx) pass else: @@ -1040,11 +5993,12 @@ def explicitIOBlock(self): self.exitRule() return localctx - class DeclvarContext(ParserRuleContext): - __slots__ = 'parser' + __slots__ = "parser" - def __init__(self, parser, parent:ParserRuleContext=None, invokingState:int=-1): + def __init__( + self, parser, parent: ParserRuleContext = None, invokingState: int = -1 + ): super().__init__(parent, invokingState) self.parser = parser @@ -1052,44 +6006,38 @@ def ID(self): return self.getToken(lfrXParser.ID, 0) def vector(self): - return self.getTypedRuleContext(lfrXParser.VectorContext,0) - + return self.getTypedRuleContext(lfrXParser.VectorContext, 0) def getRuleIndex(self): return lfrXParser.RULE_declvar - def enterRule(self, listener:ParseTreeListener): - if hasattr( listener, "enterDeclvar" ): + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterDeclvar"): listener.enterDeclvar(self) - def exitRule(self, listener:ParseTreeListener): - if hasattr( listener, "exitDeclvar" ): + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitDeclvar"): listener.exitDeclvar(self) - def accept(self, visitor:ParseTreeVisitor): - if hasattr( visitor, "visitDeclvar" ): + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitDeclvar"): return visitor.visitDeclvar(self) else: return visitor.visitChildren(self) - - - def declvar(self): - localctx = lfrXParser.DeclvarContext(self, self._ctx, self.state) self.enterRule(localctx, 14, self.RULE_declvar) - self._la = 0 # Token type + self._la = 0 # Token type try: self.enterOuterAlt(localctx, 1) self.state = 207 self._errHandler.sync(self) _la = self._input.LA(1) - if _la==lfrXParser.T__27: + if _la == lfrXParser.T__27: self.state = 206 self.vector() - self.state = 209 self.match(lfrXParser.ID) except RecognitionException as re: @@ -1100,44 +6048,39 @@ def declvar(self): self.exitRule() return localctx - class DistributionBlockContext(ParserRuleContext): - __slots__ = 'parser' + __slots__ = "parser" - def __init__(self, parser, parent:ParserRuleContext=None, invokingState:int=-1): + def __init__( + self, parser, parent: ParserRuleContext = None, invokingState: int = -1 + ): super().__init__(parent, invokingState) self.parser = parser def sensitivitylist(self): - return self.getTypedRuleContext(lfrXParser.SensitivitylistContext,0) - + return self.getTypedRuleContext(lfrXParser.SensitivitylistContext, 0) def distributionBody(self): - return self.getTypedRuleContext(lfrXParser.DistributionBodyContext,0) - + return self.getTypedRuleContext(lfrXParser.DistributionBodyContext, 0) def getRuleIndex(self): return lfrXParser.RULE_distributionBlock - def enterRule(self, listener:ParseTreeListener): - if hasattr( listener, "enterDistributionBlock" ): + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterDistributionBlock"): listener.enterDistributionBlock(self) - def exitRule(self, listener:ParseTreeListener): - if hasattr( listener, "exitDistributionBlock" ): + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitDistributionBlock"): listener.exitDistributionBlock(self) - def accept(self, visitor:ParseTreeVisitor): - if hasattr( visitor, "visitDistributionBlock" ): + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitDistributionBlock"): return visitor.visitDistributionBlock(self) else: return visitor.visitChildren(self) - - - def distributionBlock(self): - localctx = lfrXParser.DistributionBlockContext(self, self._ctx, self.state) self.enterRule(localctx, 16, self.RULE_distributionBlock) try: @@ -1164,58 +6107,68 @@ def distributionBlock(self): self.exitRule() return localctx - class DistributionBodyContext(ParserRuleContext): - __slots__ = 'parser' + __slots__ = "parser" - def __init__(self, parser, parent:ParserRuleContext=None, invokingState:int=-1): + def __init__( + self, parser, parent: ParserRuleContext = None, invokingState: int = -1 + ): super().__init__(parent, invokingState) self.parser = parser - def distributeBodyStat(self, i:int=None): + def distributeBodyStat(self, i: int = None): if i is None: return self.getTypedRuleContexts(lfrXParser.DistributeBodyStatContext) else: - return self.getTypedRuleContext(lfrXParser.DistributeBodyStatContext,i) - + return self.getTypedRuleContext(lfrXParser.DistributeBodyStatContext, i) def getRuleIndex(self): return lfrXParser.RULE_distributionBody - def enterRule(self, listener:ParseTreeListener): - if hasattr( listener, "enterDistributionBody" ): + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterDistributionBody"): listener.enterDistributionBody(self) - def exitRule(self, listener:ParseTreeListener): - if hasattr( listener, "exitDistributionBody" ): + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitDistributionBody"): listener.exitDistributionBody(self) - def accept(self, visitor:ParseTreeVisitor): - if hasattr( visitor, "visitDistributionBody" ): + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitDistributionBody"): return visitor.visitDistributionBody(self) else: return visitor.visitChildren(self) - - - def distributionBody(self): - localctx = lfrXParser.DistributionBodyContext(self, self._ctx, self.state) self.enterRule(localctx, 18, self.RULE_distributionBody) - self._la = 0 # Token type + self._la = 0 # Token type try: self.enterOuterAlt(localctx, 1) - self.state = 220 + self.state = 220 self._errHandler.sync(self) _la = self._input.LA(1) while True: self.state = 219 self.distributeBodyStat() - self.state = 222 + self.state = 222 self._errHandler.sync(self) _la = self._input.LA(1) - if not (((((_la - 13)) & ~0x3f) == 0 and ((1 << (_la - 13)) & ((1 << (lfrXParser.T__12 - 13)) | (1 << (lfrXParser.T__15 - 13)) | (1 << (lfrXParser.T__29 - 13)) | (1 << (lfrXParser.ID - 13)))) != 0)): + if not ( + ( + (((_la - 13)) & ~0x3F) == 0 + and ( + (1 << (_la - 13)) + & ( + (1 << (lfrXParser.T__12 - 13)) + | (1 << (lfrXParser.T__15 - 13)) + | (1 << (lfrXParser.T__29 - 13)) + | (1 << (lfrXParser.ID - 13)) + ) + ) + != 0 + ) + ): break except RecognitionException as re: @@ -1226,48 +6179,42 @@ def distributionBody(self): self.exitRule() return localctx - class DistributeBodyStatContext(ParserRuleContext): - __slots__ = 'parser' + __slots__ = "parser" - def __init__(self, parser, parent:ParserRuleContext=None, invokingState:int=-1): + def __init__( + self, parser, parent: ParserRuleContext = None, invokingState: int = -1 + ): super().__init__(parent, invokingState) self.parser = parser def distributionassignstat(self): - return self.getTypedRuleContext(lfrXParser.DistributionassignstatContext,0) - + return self.getTypedRuleContext(lfrXParser.DistributionassignstatContext, 0) def caseBlock(self): - return self.getTypedRuleContext(lfrXParser.CaseBlockContext,0) - + return self.getTypedRuleContext(lfrXParser.CaseBlockContext, 0) def ifElseBlock(self): - return self.getTypedRuleContext(lfrXParser.IfElseBlockContext,0) - + return self.getTypedRuleContext(lfrXParser.IfElseBlockContext, 0) def getRuleIndex(self): return lfrXParser.RULE_distributeBodyStat - def enterRule(self, listener:ParseTreeListener): - if hasattr( listener, "enterDistributeBodyStat" ): + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterDistributeBodyStat"): listener.enterDistributeBodyStat(self) - def exitRule(self, listener:ParseTreeListener): - if hasattr( listener, "exitDistributeBodyStat" ): + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitDistributeBodyStat"): listener.exitDistributeBodyStat(self) - def accept(self, visitor:ParseTreeVisitor): - if hasattr( visitor, "visitDistributeBodyStat" ): + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitDistributeBodyStat"): return visitor.visitDistributeBodyStat(self) else: return visitor.visitChildren(self) - - - def distributeBodyStat(self): - localctx = lfrXParser.DistributeBodyStatContext(self, self._ctx, self.state) self.enterRule(localctx, 20, self.RULE_distributeBodyStat) try: @@ -1300,77 +6247,70 @@ def distributeBodyStat(self): self.exitRule() return localctx - class IfElseBlockContext(ParserRuleContext): - __slots__ = 'parser' + __slots__ = "parser" - def __init__(self, parser, parent:ParserRuleContext=None, invokingState:int=-1): + def __init__( + self, parser, parent: ParserRuleContext = None, invokingState: int = -1 + ): super().__init__(parent, invokingState) self.parser = parser def ifBlock(self): - return self.getTypedRuleContext(lfrXParser.IfBlockContext,0) - + return self.getTypedRuleContext(lfrXParser.IfBlockContext, 0) - def elseIfBlock(self, i:int=None): + def elseIfBlock(self, i: int = None): if i is None: return self.getTypedRuleContexts(lfrXParser.ElseIfBlockContext) else: - return self.getTypedRuleContext(lfrXParser.ElseIfBlockContext,i) - + return self.getTypedRuleContext(lfrXParser.ElseIfBlockContext, i) def elseBlock(self): - return self.getTypedRuleContext(lfrXParser.ElseBlockContext,0) - + return self.getTypedRuleContext(lfrXParser.ElseBlockContext, 0) def getRuleIndex(self): return lfrXParser.RULE_ifElseBlock - def enterRule(self, listener:ParseTreeListener): - if hasattr( listener, "enterIfElseBlock" ): + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterIfElseBlock"): listener.enterIfElseBlock(self) - def exitRule(self, listener:ParseTreeListener): - if hasattr( listener, "exitIfElseBlock" ): + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitIfElseBlock"): listener.exitIfElseBlock(self) - def accept(self, visitor:ParseTreeVisitor): - if hasattr( visitor, "visitIfElseBlock" ): + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitIfElseBlock"): return visitor.visitIfElseBlock(self) else: return visitor.visitChildren(self) - - - def ifElseBlock(self): - localctx = lfrXParser.IfElseBlockContext(self, self._ctx, self.state) self.enterRule(localctx, 22, self.RULE_ifElseBlock) - self._la = 0 # Token type + self._la = 0 # Token type try: self.enterOuterAlt(localctx, 1) self.state = 229 self.ifBlock() self.state = 233 self._errHandler.sync(self) - _alt = self._interp.adaptivePredict(self._input,15,self._ctx) - while _alt!=2 and _alt!=ATN.INVALID_ALT_NUMBER: - if _alt==1: + _alt = self._interp.adaptivePredict(self._input, 15, self._ctx) + while _alt != 2 and _alt != ATN.INVALID_ALT_NUMBER: + if _alt == 1: self.state = 230 - self.elseIfBlock() + self.elseIfBlock() self.state = 235 self._errHandler.sync(self) - _alt = self._interp.adaptivePredict(self._input,15,self._ctx) + _alt = self._interp.adaptivePredict(self._input, 15, self._ctx) self.state = 237 self._errHandler.sync(self) _la = self._input.LA(1) - if _la==lfrXParser.T__13: + if _la == lfrXParser.T__13: self.state = 236 self.elseBlock() - except RecognitionException as re: localctx.exception = re self._errHandler.reportError(self, re) @@ -1379,44 +6319,39 @@ def ifElseBlock(self): self.exitRule() return localctx - class IfBlockContext(ParserRuleContext): - __slots__ = 'parser' + __slots__ = "parser" - def __init__(self, parser, parent:ParserRuleContext=None, invokingState:int=-1): + def __init__( + self, parser, parent: ParserRuleContext = None, invokingState: int = -1 + ): super().__init__(parent, invokingState) self.parser = parser def distributeCondition(self): - return self.getTypedRuleContext(lfrXParser.DistributeConditionContext,0) - + return self.getTypedRuleContext(lfrXParser.DistributeConditionContext, 0) def statementBlock(self): - return self.getTypedRuleContext(lfrXParser.StatementBlockContext,0) - + return self.getTypedRuleContext(lfrXParser.StatementBlockContext, 0) def getRuleIndex(self): return lfrXParser.RULE_ifBlock - def enterRule(self, listener:ParseTreeListener): - if hasattr( listener, "enterIfBlock" ): + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterIfBlock"): listener.enterIfBlock(self) - def exitRule(self, listener:ParseTreeListener): - if hasattr( listener, "exitIfBlock" ): + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitIfBlock"): listener.exitIfBlock(self) - def accept(self, visitor:ParseTreeVisitor): - if hasattr( visitor, "visitIfBlock" ): + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitIfBlock"): return visitor.visitIfBlock(self) else: return visitor.visitChildren(self) - - - def ifBlock(self): - localctx = lfrXParser.IfBlockContext(self, self._ctx, self.state) self.enterRule(localctx, 24, self.RULE_ifBlock) try: @@ -1439,40 +6374,36 @@ def ifBlock(self): self.exitRule() return localctx - class ElseBlockContext(ParserRuleContext): - __slots__ = 'parser' + __slots__ = "parser" - def __init__(self, parser, parent:ParserRuleContext=None, invokingState:int=-1): + def __init__( + self, parser, parent: ParserRuleContext = None, invokingState: int = -1 + ): super().__init__(parent, invokingState) self.parser = parser def statementBlock(self): - return self.getTypedRuleContext(lfrXParser.StatementBlockContext,0) - + return self.getTypedRuleContext(lfrXParser.StatementBlockContext, 0) def getRuleIndex(self): return lfrXParser.RULE_elseBlock - def enterRule(self, listener:ParseTreeListener): - if hasattr( listener, "enterElseBlock" ): + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterElseBlock"): listener.enterElseBlock(self) - def exitRule(self, listener:ParseTreeListener): - if hasattr( listener, "exitElseBlock" ): + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitElseBlock"): listener.exitElseBlock(self) - def accept(self, visitor:ParseTreeVisitor): - if hasattr( visitor, "visitElseBlock" ): + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitElseBlock"): return visitor.visitElseBlock(self) else: return visitor.visitChildren(self) - - - def elseBlock(self): - localctx = lfrXParser.ElseBlockContext(self, self._ctx, self.state) self.enterRule(localctx, 26, self.RULE_elseBlock) try: @@ -1489,44 +6420,39 @@ def elseBlock(self): self.exitRule() return localctx - class ElseIfBlockContext(ParserRuleContext): - __slots__ = 'parser' + __slots__ = "parser" - def __init__(self, parser, parent:ParserRuleContext=None, invokingState:int=-1): + def __init__( + self, parser, parent: ParserRuleContext = None, invokingState: int = -1 + ): super().__init__(parent, invokingState) self.parser = parser def distributeCondition(self): - return self.getTypedRuleContext(lfrXParser.DistributeConditionContext,0) - + return self.getTypedRuleContext(lfrXParser.DistributeConditionContext, 0) def statementBlock(self): - return self.getTypedRuleContext(lfrXParser.StatementBlockContext,0) - + return self.getTypedRuleContext(lfrXParser.StatementBlockContext, 0) def getRuleIndex(self): return lfrXParser.RULE_elseIfBlock - def enterRule(self, listener:ParseTreeListener): - if hasattr( listener, "enterElseIfBlock" ): + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterElseIfBlock"): listener.enterElseIfBlock(self) - def exitRule(self, listener:ParseTreeListener): - if hasattr( listener, "exitElseIfBlock" ): + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitElseIfBlock"): listener.exitElseIfBlock(self) - def accept(self, visitor:ParseTreeVisitor): - if hasattr( visitor, "visitElseIfBlock" ): + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitElseIfBlock"): return visitor.visitElseIfBlock(self) else: return visitor.visitChildren(self) - - - def elseIfBlock(self): - localctx = lfrXParser.ElseIfBlockContext(self, self._ctx, self.state) self.enterRule(localctx, 28, self.RULE_elseIfBlock) try: @@ -1551,48 +6477,44 @@ def elseIfBlock(self): self.exitRule() return localctx - class DistributeConditionContext(ParserRuleContext): - __slots__ = 'parser' + __slots__ = "parser" - def __init__(self, parser, parent:ParserRuleContext=None, invokingState:int=-1): + def __init__( + self, parser, parent: ParserRuleContext = None, invokingState: int = -1 + ): super().__init__(parent, invokingState) self.parser = parser def lhs(self): - return self.getTypedRuleContext(lfrXParser.LhsContext,0) - + return self.getTypedRuleContext(lfrXParser.LhsContext, 0) def binary_module_path_operator(self): - return self.getTypedRuleContext(lfrXParser.Binary_module_path_operatorContext,0) - + return self.getTypedRuleContext( + lfrXParser.Binary_module_path_operatorContext, 0 + ) def distvalue(self): - return self.getTypedRuleContext(lfrXParser.DistvalueContext,0) - + return self.getTypedRuleContext(lfrXParser.DistvalueContext, 0) def getRuleIndex(self): return lfrXParser.RULE_distributeCondition - def enterRule(self, listener:ParseTreeListener): - if hasattr( listener, "enterDistributeCondition" ): + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterDistributeCondition"): listener.enterDistributeCondition(self) - def exitRule(self, listener:ParseTreeListener): - if hasattr( listener, "exitDistributeCondition" ): + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitDistributeCondition"): listener.exitDistributeCondition(self) - def accept(self, visitor:ParseTreeVisitor): - if hasattr( visitor, "visitDistributeCondition" ): + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitDistributeCondition"): return visitor.visitDistributeCondition(self) else: return visitor.visitChildren(self) - - - def distributeCondition(self): - localctx = lfrXParser.DistributeConditionContext(self, self._ctx, self.state) self.enterRule(localctx, 30, self.RULE_distributeCondition) try: @@ -1611,46 +6533,46 @@ def distributeCondition(self): self.exitRule() return localctx - class StatementBlockContext(ParserRuleContext): - __slots__ = 'parser' + __slots__ = "parser" - def __init__(self, parser, parent:ParserRuleContext=None, invokingState:int=-1): + def __init__( + self, parser, parent: ParserRuleContext = None, invokingState: int = -1 + ): super().__init__(parent, invokingState) self.parser = parser - def distributionassignstat(self, i:int=None): + def distributionassignstat(self, i: int = None): if i is None: - return self.getTypedRuleContexts(lfrXParser.DistributionassignstatContext) + return self.getTypedRuleContexts( + lfrXParser.DistributionassignstatContext + ) else: - return self.getTypedRuleContext(lfrXParser.DistributionassignstatContext,i) - + return self.getTypedRuleContext( + lfrXParser.DistributionassignstatContext, i + ) def getRuleIndex(self): return lfrXParser.RULE_statementBlock - def enterRule(self, listener:ParseTreeListener): - if hasattr( listener, "enterStatementBlock" ): + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterStatementBlock"): listener.enterStatementBlock(self) - def exitRule(self, listener:ParseTreeListener): - if hasattr( listener, "exitStatementBlock" ): + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitStatementBlock"): listener.exitStatementBlock(self) - def accept(self, visitor:ParseTreeVisitor): - if hasattr( visitor, "visitStatementBlock" ): + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitStatementBlock"): return visitor.visitStatementBlock(self) else: return visitor.visitChildren(self) - - - def statementBlock(self): - localctx = lfrXParser.StatementBlockContext(self, self._ctx, self.state) self.enterRule(localctx, 32, self.RULE_statementBlock) - self._la = 0 # Token type + self._la = 0 # Token type try: self.state = 268 self._errHandler.sync(self) @@ -1659,16 +6581,16 @@ def statementBlock(self): self.enterOuterAlt(localctx, 1) self.state = 259 self.match(lfrXParser.T__10) - self.state = 261 + self.state = 261 self._errHandler.sync(self) _la = self._input.LA(1) while True: self.state = 260 self.distributionassignstat() - self.state = 263 + self.state = 263 self._errHandler.sync(self) _la = self._input.LA(1) - if not (_la==lfrXParser.T__29 or _la==lfrXParser.ID): + if not (_la == lfrXParser.T__29 or _la == lfrXParser.ID): break self.state = 265 @@ -1690,78 +6612,86 @@ def statementBlock(self): self.exitRule() return localctx - class CaseBlockContext(ParserRuleContext): - __slots__ = 'parser' + __slots__ = "parser" - def __init__(self, parser, parent:ParserRuleContext=None, invokingState:int=-1): + def __init__( + self, parser, parent: ParserRuleContext = None, invokingState: int = -1 + ): super().__init__(parent, invokingState) self.parser = parser def caseBlockHeader(self): - return self.getTypedRuleContext(lfrXParser.CaseBlockHeaderContext,0) + return self.getTypedRuleContext(lfrXParser.CaseBlockHeaderContext, 0) - - def casestat(self, i:int=None): + def casestat(self, i: int = None): if i is None: return self.getTypedRuleContexts(lfrXParser.CasestatContext) else: - return self.getTypedRuleContext(lfrXParser.CasestatContext,i) - + return self.getTypedRuleContext(lfrXParser.CasestatContext, i) def defaultCaseStat(self): - return self.getTypedRuleContext(lfrXParser.DefaultCaseStatContext,0) - + return self.getTypedRuleContext(lfrXParser.DefaultCaseStatContext, 0) def getRuleIndex(self): return lfrXParser.RULE_caseBlock - def enterRule(self, listener:ParseTreeListener): - if hasattr( listener, "enterCaseBlock" ): + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterCaseBlock"): listener.enterCaseBlock(self) - def exitRule(self, listener:ParseTreeListener): - if hasattr( listener, "exitCaseBlock" ): + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitCaseBlock"): listener.exitCaseBlock(self) - def accept(self, visitor:ParseTreeVisitor): - if hasattr( visitor, "visitCaseBlock" ): + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitCaseBlock"): return visitor.visitCaseBlock(self) else: return visitor.visitChildren(self) - - - def caseBlock(self): - localctx = lfrXParser.CaseBlockContext(self, self._ctx, self.state) self.enterRule(localctx, 34, self.RULE_caseBlock) - self._la = 0 # Token type + self._la = 0 # Token type try: self.enterOuterAlt(localctx, 1) self.state = 270 self.caseBlockHeader() - self.state = 272 + self.state = 272 self._errHandler.sync(self) _la = self._input.LA(1) while True: self.state = 271 self.casestat() - self.state = 274 + self.state = 274 self._errHandler.sync(self) _la = self._input.LA(1) - if not (((((_la - 71)) & ~0x3f) == 0 and ((1 << (_la - 71)) & ((1 << (lfrXParser.Real_number - 71)) | (1 << (lfrXParser.Decimal_number - 71)) | (1 << (lfrXParser.Binary_number - 71)) | (1 << (lfrXParser.Octal_number - 71)) | (1 << (lfrXParser.Hex_number - 71)))) != 0)): + if not ( + ( + (((_la - 71)) & ~0x3F) == 0 + and ( + (1 << (_la - 71)) + & ( + (1 << (lfrXParser.Real_number - 71)) + | (1 << (lfrXParser.Decimal_number - 71)) + | (1 << (lfrXParser.Binary_number - 71)) + | (1 << (lfrXParser.Octal_number - 71)) + | (1 << (lfrXParser.Hex_number - 71)) + ) + ) + != 0 + ) + ): break self.state = 277 self._errHandler.sync(self) _la = self._input.LA(1) - if _la==lfrXParser.T__17: + if _la == lfrXParser.T__17: self.state = 276 self.defaultCaseStat() - self.state = 279 self.match(lfrXParser.T__14) except RecognitionException as re: @@ -1772,40 +6702,36 @@ def caseBlock(self): self.exitRule() return localctx - class CaseBlockHeaderContext(ParserRuleContext): - __slots__ = 'parser' + __slots__ = "parser" - def __init__(self, parser, parent:ParserRuleContext=None, invokingState:int=-1): + def __init__( + self, parser, parent: ParserRuleContext = None, invokingState: int = -1 + ): super().__init__(parent, invokingState) self.parser = parser def lhs(self): - return self.getTypedRuleContext(lfrXParser.LhsContext,0) - + return self.getTypedRuleContext(lfrXParser.LhsContext, 0) def getRuleIndex(self): return lfrXParser.RULE_caseBlockHeader - def enterRule(self, listener:ParseTreeListener): - if hasattr( listener, "enterCaseBlockHeader" ): + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterCaseBlockHeader"): listener.enterCaseBlockHeader(self) - def exitRule(self, listener:ParseTreeListener): - if hasattr( listener, "exitCaseBlockHeader" ): + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitCaseBlockHeader"): listener.exitCaseBlockHeader(self) - def accept(self, visitor:ParseTreeVisitor): - if hasattr( visitor, "visitCaseBlockHeader" ): + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitCaseBlockHeader"): return visitor.visitCaseBlockHeader(self) else: return visitor.visitChildren(self) - - - def caseBlockHeader(self): - localctx = lfrXParser.CaseBlockHeaderContext(self, self._ctx, self.state) self.enterRule(localctx, 36, self.RULE_caseBlockHeader) try: @@ -1826,44 +6752,39 @@ def caseBlockHeader(self): self.exitRule() return localctx - class CasestatContext(ParserRuleContext): - __slots__ = 'parser' + __slots__ = "parser" - def __init__(self, parser, parent:ParserRuleContext=None, invokingState:int=-1): + def __init__( + self, parser, parent: ParserRuleContext = None, invokingState: int = -1 + ): super().__init__(parent, invokingState) self.parser = parser def distvalue(self): - return self.getTypedRuleContext(lfrXParser.DistvalueContext,0) - + return self.getTypedRuleContext(lfrXParser.DistvalueContext, 0) def statementBlock(self): - return self.getTypedRuleContext(lfrXParser.StatementBlockContext,0) - + return self.getTypedRuleContext(lfrXParser.StatementBlockContext, 0) def getRuleIndex(self): return lfrXParser.RULE_casestat - def enterRule(self, listener:ParseTreeListener): - if hasattr( listener, "enterCasestat" ): + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterCasestat"): listener.enterCasestat(self) - def exitRule(self, listener:ParseTreeListener): - if hasattr( listener, "exitCasestat" ): + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitCasestat"): listener.exitCasestat(self) - def accept(self, visitor:ParseTreeVisitor): - if hasattr( visitor, "visitCasestat" ): + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitCasestat"): return visitor.visitCasestat(self) else: return visitor.visitChildren(self) - - - def casestat(self): - localctx = lfrXParser.CasestatContext(self, self._ctx, self.state) self.enterRule(localctx, 38, self.RULE_casestat) try: @@ -1882,40 +6803,36 @@ def casestat(self): self.exitRule() return localctx - class DefaultCaseStatContext(ParserRuleContext): - __slots__ = 'parser' + __slots__ = "parser" - def __init__(self, parser, parent:ParserRuleContext=None, invokingState:int=-1): + def __init__( + self, parser, parent: ParserRuleContext = None, invokingState: int = -1 + ): super().__init__(parent, invokingState) self.parser = parser def statementBlock(self): - return self.getTypedRuleContext(lfrXParser.StatementBlockContext,0) - + return self.getTypedRuleContext(lfrXParser.StatementBlockContext, 0) def getRuleIndex(self): return lfrXParser.RULE_defaultCaseStat - def enterRule(self, listener:ParseTreeListener): - if hasattr( listener, "enterDefaultCaseStat" ): + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterDefaultCaseStat"): listener.enterDefaultCaseStat(self) - def exitRule(self, listener:ParseTreeListener): - if hasattr( listener, "exitDefaultCaseStat" ): + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitDefaultCaseStat"): listener.exitDefaultCaseStat(self) - def accept(self, visitor:ParseTreeVisitor): - if hasattr( visitor, "visitDefaultCaseStat" ): + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitDefaultCaseStat"): return visitor.visitDefaultCaseStat(self) else: return visitor.visitChildren(self) - - - def defaultCaseStat(self): - localctx = lfrXParser.DefaultCaseStatContext(self, self._ctx, self.state) self.enterRule(localctx, 40, self.RULE_defaultCaseStat) try: @@ -1934,40 +6851,36 @@ def defaultCaseStat(self): self.exitRule() return localctx - class DistvalueContext(ParserRuleContext): - __slots__ = 'parser' + __slots__ = "parser" - def __init__(self, parser, parent:ParserRuleContext=None, invokingState:int=-1): + def __init__( + self, parser, parent: ParserRuleContext = None, invokingState: int = -1 + ): super().__init__(parent, invokingState) self.parser = parser def number(self): - return self.getTypedRuleContext(lfrXParser.NumberContext,0) - + return self.getTypedRuleContext(lfrXParser.NumberContext, 0) def getRuleIndex(self): return lfrXParser.RULE_distvalue - def enterRule(self, listener:ParseTreeListener): - if hasattr( listener, "enterDistvalue" ): + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterDistvalue"): listener.enterDistvalue(self) - def exitRule(self, listener:ParseTreeListener): - if hasattr( listener, "exitDistvalue" ): + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitDistvalue"): listener.exitDistvalue(self) - def accept(self, visitor:ParseTreeVisitor): - if hasattr( visitor, "visitDistvalue" ): + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitDistvalue"): return visitor.visitDistvalue(self) else: return visitor.visitChildren(self) - - - def distvalue(self): - localctx = lfrXParser.DistvalueContext(self, self._ctx, self.state) self.enterRule(localctx, 42, self.RULE_distvalue) try: @@ -1982,52 +6895,45 @@ def distvalue(self): self.exitRule() return localctx - class DistributionassignstatContext(ParserRuleContext): - __slots__ = 'parser' + __slots__ = "parser" - def __init__(self, parser, parent:ParserRuleContext=None, invokingState:int=-1): + def __init__( + self, parser, parent: ParserRuleContext = None, invokingState: int = -1 + ): super().__init__(parent, invokingState) self.parser = parser def lhs(self): - return self.getTypedRuleContext(lfrXParser.LhsContext,0) - + return self.getTypedRuleContext(lfrXParser.LhsContext, 0) def number(self): - return self.getTypedRuleContext(lfrXParser.NumberContext,0) - + return self.getTypedRuleContext(lfrXParser.NumberContext, 0) def variables(self): - return self.getTypedRuleContext(lfrXParser.VariablesContext,0) - + return self.getTypedRuleContext(lfrXParser.VariablesContext, 0) def expression(self): - return self.getTypedRuleContext(lfrXParser.ExpressionContext,0) - + return self.getTypedRuleContext(lfrXParser.ExpressionContext, 0) def getRuleIndex(self): return lfrXParser.RULE_distributionassignstat - def enterRule(self, listener:ParseTreeListener): - if hasattr( listener, "enterDistributionassignstat" ): + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterDistributionassignstat"): listener.enterDistributionassignstat(self) - def exitRule(self, listener:ParseTreeListener): - if hasattr( listener, "exitDistributionassignstat" ): + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitDistributionassignstat"): listener.exitDistributionassignstat(self) - def accept(self, visitor:ParseTreeVisitor): - if hasattr( visitor, "visitDistributionassignstat" ): + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitDistributionassignstat"): return visitor.visitDistributionassignstat(self) else: return visitor.visitChildren(self) - - - def distributionassignstat(self): - localctx = lfrXParser.DistributionassignstatContext(self, self._ctx, self.state) self.enterRule(localctx, 44, self.RULE_distributionassignstat) try: @@ -2038,7 +6944,7 @@ def distributionassignstat(self): self.match(lfrXParser.T__18) self.state = 301 self._errHandler.sync(self) - la_ = self._interp.adaptivePredict(self._input,21,self._ctx) + la_ = self._interp.adaptivePredict(self._input, 21, self._ctx) if la_ == 1: self.state = 298 self.number() @@ -2054,7 +6960,6 @@ def distributionassignstat(self): self.expression() pass - self.state = 303 self.match(lfrXParser.T__4) except RecognitionException as re: @@ -2065,46 +6970,42 @@ def distributionassignstat(self): self.exitRule() return localctx - class SensitivitylistContext(ParserRuleContext): - __slots__ = 'parser' + __slots__ = "parser" - def __init__(self, parser, parent:ParserRuleContext=None, invokingState:int=-1): + def __init__( + self, parser, parent: ParserRuleContext = None, invokingState: int = -1 + ): super().__init__(parent, invokingState) self.parser = parser - def signal(self, i:int=None): + def signal(self, i: int = None): if i is None: return self.getTypedRuleContexts(lfrXParser.SignalContext) else: - return self.getTypedRuleContext(lfrXParser.SignalContext,i) - + return self.getTypedRuleContext(lfrXParser.SignalContext, i) def getRuleIndex(self): return lfrXParser.RULE_sensitivitylist - def enterRule(self, listener:ParseTreeListener): - if hasattr( listener, "enterSensitivitylist" ): + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterSensitivitylist"): listener.enterSensitivitylist(self) - def exitRule(self, listener:ParseTreeListener): - if hasattr( listener, "exitSensitivitylist" ): + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitSensitivitylist"): listener.exitSensitivitylist(self) - def accept(self, visitor:ParseTreeVisitor): - if hasattr( visitor, "visitSensitivitylist" ): + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitSensitivitylist"): return visitor.visitSensitivitylist(self) else: return visitor.visitChildren(self) - - - def sensitivitylist(self): - localctx = lfrXParser.SensitivitylistContext(self, self._ctx, self.state) self.enterRule(localctx, 46, self.RULE_sensitivitylist) - self._la = 0 # Token type + self._la = 0 # Token type try: self.enterOuterAlt(localctx, 1) self.state = 305 @@ -2112,7 +7013,7 @@ def sensitivitylist(self): self.state = 310 self._errHandler.sync(self) _la = self._input.LA(1) - while _la==lfrXParser.T__5: + while _la == lfrXParser.T__5: self.state = 306 self.match(lfrXParser.T__5) self.state = 307 @@ -2129,11 +7030,12 @@ def sensitivitylist(self): self.exitRule() return localctx - class SignalContext(ParserRuleContext): - __slots__ = 'parser' + __slots__ = "parser" - def __init__(self, parser, parent:ParserRuleContext=None, invokingState:int=-1): + def __init__( + self, parser, parent: ParserRuleContext = None, invokingState: int = -1 + ): super().__init__(parent, invokingState) self.parser = parser @@ -2141,34 +7043,29 @@ def ID(self): return self.getToken(lfrXParser.ID, 0) def vector(self): - return self.getTypedRuleContext(lfrXParser.VectorContext,0) - + return self.getTypedRuleContext(lfrXParser.VectorContext, 0) def getRuleIndex(self): return lfrXParser.RULE_signal - def enterRule(self, listener:ParseTreeListener): - if hasattr( listener, "enterSignal" ): + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterSignal"): listener.enterSignal(self) - def exitRule(self, listener:ParseTreeListener): - if hasattr( listener, "exitSignal" ): + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitSignal"): listener.exitSignal(self) - def accept(self, visitor:ParseTreeVisitor): - if hasattr( visitor, "visitSignal" ): + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitSignal"): return visitor.visitSignal(self) else: return visitor.visitChildren(self) - - - def signal(self): - localctx = lfrXParser.SignalContext(self, self._ctx, self.state) self.enterRule(localctx, 48, self.RULE_signal) - self._la = 0 # Token type + self._la = 0 # Token type try: self.enterOuterAlt(localctx, 1) self.state = 313 @@ -2176,11 +7073,10 @@ def signal(self): self.state = 315 self._errHandler.sync(self) _la = self._input.LA(1) - if _la==lfrXParser.T__27: + if _la == lfrXParser.T__27: self.state = 314 self.vector() - except RecognitionException as re: localctx.exception = re self._errHandler.reportError(self, re) @@ -2189,51 +7085,57 @@ def signal(self): self.exitRule() return localctx - class StatementsContext(ParserRuleContext): - __slots__ = 'parser' + __slots__ = "parser" - def __init__(self, parser, parent:ParserRuleContext=None, invokingState:int=-1): + def __init__( + self, parser, parent: ParserRuleContext = None, invokingState: int = -1 + ): super().__init__(parent, invokingState) self.parser = parser def statement(self): - return self.getTypedRuleContext(lfrXParser.StatementContext,0) - + return self.getTypedRuleContext(lfrXParser.StatementContext, 0) def technologydirectives(self): - return self.getTypedRuleContext(lfrXParser.TechnologydirectivesContext,0) - + return self.getTypedRuleContext(lfrXParser.TechnologydirectivesContext, 0) def getRuleIndex(self): return lfrXParser.RULE_statements - def enterRule(self, listener:ParseTreeListener): - if hasattr( listener, "enterStatements" ): + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterStatements"): listener.enterStatements(self) - def exitRule(self, listener:ParseTreeListener): - if hasattr( listener, "exitStatements" ): + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitStatements"): listener.exitStatements(self) - def accept(self, visitor:ParseTreeVisitor): - if hasattr( visitor, "visitStatements" ): + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitStatements"): return visitor.visitStatements(self) else: return visitor.visitChildren(self) - - - def statements(self): - localctx = lfrXParser.StatementsContext(self, self._ctx, self.state) self.enterRule(localctx, 50, self.RULE_statements) try: self.state = 321 self._errHandler.sync(self) token = self._input.LA(1) - if token in [lfrXParser.T__6, lfrXParser.T__7, lfrXParser.T__8, lfrXParser.T__20, lfrXParser.T__21, lfrXParser.T__22, lfrXParser.T__23, lfrXParser.T__24, lfrXParser.T__25, lfrXParser.ID]: + if token in [ + lfrXParser.T__6, + lfrXParser.T__7, + lfrXParser.T__8, + lfrXParser.T__20, + lfrXParser.T__21, + lfrXParser.T__22, + lfrXParser.T__23, + lfrXParser.T__24, + lfrXParser.T__25, + lfrXParser.ID, + ]: self.enterOuterAlt(localctx, 1) self.state = 317 self.statement() @@ -2256,62 +7158,56 @@ def statements(self): self.exitRule() return localctx - class StatementContext(ParserRuleContext): - __slots__ = 'parser' + __slots__ = "parser" - def __init__(self, parser, parent:ParserRuleContext=None, invokingState:int=-1): + def __init__( + self, parser, parent: ParserRuleContext = None, invokingState: int = -1 + ): super().__init__(parent, invokingState) self.parser = parser def ioassignstat(self): - return self.getTypedRuleContext(lfrXParser.IoassignstatContext,0) - + return self.getTypedRuleContext(lfrXParser.IoassignstatContext, 0) def assignstat(self): - return self.getTypedRuleContext(lfrXParser.AssignstatContext,0) - + return self.getTypedRuleContext(lfrXParser.AssignstatContext, 0) def tempvariablesstat(self): - return self.getTypedRuleContext(lfrXParser.TempvariablesstatContext,0) - + return self.getTypedRuleContext(lfrXParser.TempvariablesstatContext, 0) def literalassignstat(self): - return self.getTypedRuleContext(lfrXParser.LiteralassignstatContext,0) - + return self.getTypedRuleContext(lfrXParser.LiteralassignstatContext, 0) def moduleinstantiationstat(self): - return self.getTypedRuleContext(lfrXParser.ModuleinstantiationstatContext,0) - + return self.getTypedRuleContext( + lfrXParser.ModuleinstantiationstatContext, 0 + ) def getRuleIndex(self): return lfrXParser.RULE_statement - def enterRule(self, listener:ParseTreeListener): - if hasattr( listener, "enterStatement" ): + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterStatement"): listener.enterStatement(self) - def exitRule(self, listener:ParseTreeListener): - if hasattr( listener, "exitStatement" ): + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitStatement"): listener.exitStatement(self) - def accept(self, visitor:ParseTreeVisitor): - if hasattr( visitor, "visitStatement" ): + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitStatement"): return visitor.visitStatement(self) else: return visitor.visitChildren(self) - - - def statement(self): - localctx = lfrXParser.StatementContext(self, self._ctx, self.state) self.enterRule(localctx, 52, self.RULE_statement) try: self.state = 328 self._errHandler.sync(self) - la_ = self._interp.adaptivePredict(self._input,25,self._ctx) + la_ = self._interp.adaptivePredict(self._input, 25, self._ctx) if la_ == 1: self.enterOuterAlt(localctx, 1) self.state = 323 @@ -2342,7 +7238,6 @@ def statement(self): self.moduleinstantiationstat() pass - except RecognitionException as re: localctx.exception = re self._errHandler.reportError(self, re) @@ -2351,49 +7246,45 @@ def statement(self): self.exitRule() return localctx - class ModuleinstantiationstatContext(ParserRuleContext): - __slots__ = 'parser' + __slots__ = "parser" - def __init__(self, parser, parent:ParserRuleContext=None, invokingState:int=-1): + def __init__( + self, parser, parent: ParserRuleContext = None, invokingState: int = -1 + ): super().__init__(parent, invokingState) self.parser = parser def moduletype(self): - return self.getTypedRuleContext(lfrXParser.ModuletypeContext,0) - + return self.getTypedRuleContext(lfrXParser.ModuletypeContext, 0) def instancename(self): - return self.getTypedRuleContext(lfrXParser.InstancenameContext,0) - + return self.getTypedRuleContext(lfrXParser.InstancenameContext, 0) def instanceioblock(self): - return self.getTypedRuleContext(lfrXParser.InstanceioblockContext,0) - + return self.getTypedRuleContext(lfrXParser.InstanceioblockContext, 0) def getRuleIndex(self): return lfrXParser.RULE_moduleinstantiationstat - def enterRule(self, listener:ParseTreeListener): - if hasattr( listener, "enterModuleinstantiationstat" ): + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterModuleinstantiationstat"): listener.enterModuleinstantiationstat(self) - def exitRule(self, listener:ParseTreeListener): - if hasattr( listener, "exitModuleinstantiationstat" ): + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitModuleinstantiationstat"): listener.exitModuleinstantiationstat(self) - def accept(self, visitor:ParseTreeVisitor): - if hasattr( visitor, "visitModuleinstantiationstat" ): + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitModuleinstantiationstat"): return visitor.visitModuleinstantiationstat(self) else: return visitor.visitChildren(self) - - - def moduleinstantiationstat(self): - - localctx = lfrXParser.ModuleinstantiationstatContext(self, self._ctx, self.state) + localctx = lfrXParser.ModuleinstantiationstatContext( + self, self._ctx, self.state + ) self.enterRule(localctx, 54, self.RULE_moduleinstantiationstat) try: self.enterOuterAlt(localctx, 1) @@ -2415,44 +7306,39 @@ def moduleinstantiationstat(self): self.exitRule() return localctx - class InstanceioblockContext(ParserRuleContext): - __slots__ = 'parser' + __slots__ = "parser" - def __init__(self, parser, parent:ParserRuleContext=None, invokingState:int=-1): + def __init__( + self, parser, parent: ParserRuleContext = None, invokingState: int = -1 + ): super().__init__(parent, invokingState) self.parser = parser def orderedioblock(self): - return self.getTypedRuleContext(lfrXParser.OrderedioblockContext,0) - + return self.getTypedRuleContext(lfrXParser.OrderedioblockContext, 0) def unorderedioblock(self): - return self.getTypedRuleContext(lfrXParser.UnorderedioblockContext,0) - + return self.getTypedRuleContext(lfrXParser.UnorderedioblockContext, 0) def getRuleIndex(self): return lfrXParser.RULE_instanceioblock - def enterRule(self, listener:ParseTreeListener): - if hasattr( listener, "enterInstanceioblock" ): + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterInstanceioblock"): listener.enterInstanceioblock(self) - def exitRule(self, listener:ParseTreeListener): - if hasattr( listener, "exitInstanceioblock" ): + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitInstanceioblock"): listener.exitInstanceioblock(self) - def accept(self, visitor:ParseTreeVisitor): - if hasattr( visitor, "visitInstanceioblock" ): + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitInstanceioblock"): return visitor.visitInstanceioblock(self) else: return visitor.visitChildren(self) - - - def instanceioblock(self): - localctx = lfrXParser.InstanceioblockContext(self, self._ctx, self.state) self.enterRule(localctx, 56, self.RULE_instanceioblock) try: @@ -2480,46 +7366,42 @@ def instanceioblock(self): self.exitRule() return localctx - class OrderedioblockContext(ParserRuleContext): - __slots__ = 'parser' + __slots__ = "parser" - def __init__(self, parser, parent:ParserRuleContext=None, invokingState:int=-1): + def __init__( + self, parser, parent: ParserRuleContext = None, invokingState: int = -1 + ): super().__init__(parent, invokingState) self.parser = parser - def vectorvar(self, i:int=None): + def vectorvar(self, i: int = None): if i is None: return self.getTypedRuleContexts(lfrXParser.VectorvarContext) else: - return self.getTypedRuleContext(lfrXParser.VectorvarContext,i) - + return self.getTypedRuleContext(lfrXParser.VectorvarContext, i) def getRuleIndex(self): return lfrXParser.RULE_orderedioblock - def enterRule(self, listener:ParseTreeListener): - if hasattr( listener, "enterOrderedioblock" ): + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterOrderedioblock"): listener.enterOrderedioblock(self) - def exitRule(self, listener:ParseTreeListener): - if hasattr( listener, "exitOrderedioblock" ): + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitOrderedioblock"): listener.exitOrderedioblock(self) - def accept(self, visitor:ParseTreeVisitor): - if hasattr( visitor, "visitOrderedioblock" ): + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitOrderedioblock"): return visitor.visitOrderedioblock(self) else: return visitor.visitChildren(self) - - - def orderedioblock(self): - localctx = lfrXParser.OrderedioblockContext(self, self._ctx, self.state) self.enterRule(localctx, 58, self.RULE_orderedioblock) - self._la = 0 # Token type + self._la = 0 # Token type try: self.enterOuterAlt(localctx, 1) self.state = 340 @@ -2527,7 +7409,7 @@ def orderedioblock(self): self.state = 345 self._errHandler.sync(self) _la = self._input.LA(1) - while _la==lfrXParser.T__5: + while _la == lfrXParser.T__5: self.state = 341 self.match(lfrXParser.T__5) self.state = 342 @@ -2544,46 +7426,46 @@ def orderedioblock(self): self.exitRule() return localctx - class UnorderedioblockContext(ParserRuleContext): - __slots__ = 'parser' + __slots__ = "parser" - def __init__(self, parser, parent:ParserRuleContext=None, invokingState:int=-1): + def __init__( + self, parser, parent: ParserRuleContext = None, invokingState: int = -1 + ): super().__init__(parent, invokingState) self.parser = parser - def explicitinstanceiomapping(self, i:int=None): + def explicitinstanceiomapping(self, i: int = None): if i is None: - return self.getTypedRuleContexts(lfrXParser.ExplicitinstanceiomappingContext) + return self.getTypedRuleContexts( + lfrXParser.ExplicitinstanceiomappingContext + ) else: - return self.getTypedRuleContext(lfrXParser.ExplicitinstanceiomappingContext,i) - + return self.getTypedRuleContext( + lfrXParser.ExplicitinstanceiomappingContext, i + ) def getRuleIndex(self): return lfrXParser.RULE_unorderedioblock - def enterRule(self, listener:ParseTreeListener): - if hasattr( listener, "enterUnorderedioblock" ): + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterUnorderedioblock"): listener.enterUnorderedioblock(self) - def exitRule(self, listener:ParseTreeListener): - if hasattr( listener, "exitUnorderedioblock" ): + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitUnorderedioblock"): listener.exitUnorderedioblock(self) - def accept(self, visitor:ParseTreeVisitor): - if hasattr( visitor, "visitUnorderedioblock" ): + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitUnorderedioblock"): return visitor.visitUnorderedioblock(self) else: return visitor.visitChildren(self) - - - def unorderedioblock(self): - localctx = lfrXParser.UnorderedioblockContext(self, self._ctx, self.state) self.enterRule(localctx, 60, self.RULE_unorderedioblock) - self._la = 0 # Token type + self._la = 0 # Token type try: self.enterOuterAlt(localctx, 1) self.state = 348 @@ -2591,7 +7473,7 @@ def unorderedioblock(self): self.state = 353 self._errHandler.sync(self) _la = self._input.LA(1) - while _la==lfrXParser.T__5: + while _la == lfrXParser.T__5: self.state = 349 self.match(lfrXParser.T__5) self.state = 350 @@ -2608,11 +7490,12 @@ def unorderedioblock(self): self.exitRule() return localctx - class ExplicitinstanceiomappingContext(ParserRuleContext): - __slots__ = 'parser' + __slots__ = "parser" - def __init__(self, parser, parent:ParserRuleContext=None, invokingState:int=-1): + def __init__( + self, parser, parent: ParserRuleContext = None, invokingState: int = -1 + ): super().__init__(parent, invokingState) self.parser = parser @@ -2620,32 +7503,29 @@ def ID(self): return self.getToken(lfrXParser.ID, 0) def variables(self): - return self.getTypedRuleContext(lfrXParser.VariablesContext,0) - + return self.getTypedRuleContext(lfrXParser.VariablesContext, 0) def getRuleIndex(self): return lfrXParser.RULE_explicitinstanceiomapping - def enterRule(self, listener:ParseTreeListener): - if hasattr( listener, "enterExplicitinstanceiomapping" ): + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterExplicitinstanceiomapping"): listener.enterExplicitinstanceiomapping(self) - def exitRule(self, listener:ParseTreeListener): - if hasattr( listener, "exitExplicitinstanceiomapping" ): + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitExplicitinstanceiomapping"): listener.exitExplicitinstanceiomapping(self) - def accept(self, visitor:ParseTreeVisitor): - if hasattr( visitor, "visitExplicitinstanceiomapping" ): + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitExplicitinstanceiomapping"): return visitor.visitExplicitinstanceiomapping(self) else: return visitor.visitChildren(self) - - - def explicitinstanceiomapping(self): - - localctx = lfrXParser.ExplicitinstanceiomappingContext(self, self._ctx, self.state) + localctx = lfrXParser.ExplicitinstanceiomappingContext( + self, self._ctx, self.state + ) self.enterRule(localctx, 62, self.RULE_explicitinstanceiomapping) try: self.enterOuterAlt(localctx, 1) @@ -2667,11 +7547,12 @@ def explicitinstanceiomapping(self): self.exitRule() return localctx - class InstancenameContext(ParserRuleContext): - __slots__ = 'parser' + __slots__ = "parser" - def __init__(self, parser, parent:ParserRuleContext=None, invokingState:int=-1): + def __init__( + self, parser, parent: ParserRuleContext = None, invokingState: int = -1 + ): super().__init__(parent, invokingState) self.parser = parser @@ -2681,25 +7562,21 @@ def ID(self): def getRuleIndex(self): return lfrXParser.RULE_instancename - def enterRule(self, listener:ParseTreeListener): - if hasattr( listener, "enterInstancename" ): + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterInstancename"): listener.enterInstancename(self) - def exitRule(self, listener:ParseTreeListener): - if hasattr( listener, "exitInstancename" ): + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitInstancename"): listener.exitInstancename(self) - def accept(self, visitor:ParseTreeVisitor): - if hasattr( visitor, "visitInstancename" ): + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitInstancename"): return visitor.visitInstancename(self) else: return visitor.visitChildren(self) - - - def instancename(self): - localctx = lfrXParser.InstancenameContext(self, self._ctx, self.state) self.enterRule(localctx, 64, self.RULE_instancename) try: @@ -2714,11 +7591,12 @@ def instancename(self): self.exitRule() return localctx - class ModuletypeContext(ParserRuleContext): - __slots__ = 'parser' + __slots__ = "parser" - def __init__(self, parser, parent:ParserRuleContext=None, invokingState:int=-1): + def __init__( + self, parser, parent: ParserRuleContext = None, invokingState: int = -1 + ): super().__init__(parent, invokingState) self.parser = parser @@ -2728,25 +7606,21 @@ def ID(self): def getRuleIndex(self): return lfrXParser.RULE_moduletype - def enterRule(self, listener:ParseTreeListener): - if hasattr( listener, "enterModuletype" ): + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterModuletype"): listener.enterModuletype(self) - def exitRule(self, listener:ParseTreeListener): - if hasattr( listener, "exitModuletype" ): + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitModuletype"): listener.exitModuletype(self) - def accept(self, visitor:ParseTreeVisitor): - if hasattr( visitor, "visitModuletype" ): + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitModuletype"): return visitor.visitModuletype(self) else: return visitor.visitChildren(self) - - - def moduletype(self): - localctx = lfrXParser.ModuletypeContext(self, self._ctx, self.state) self.enterRule(localctx, 66, self.RULE_moduletype) try: @@ -2761,56 +7635,48 @@ def moduletype(self): self.exitRule() return localctx - class TempvariablesstatContext(ParserRuleContext): - __slots__ = 'parser' + __slots__ = "parser" - def __init__(self, parser, parent:ParserRuleContext=None, invokingState:int=-1): + def __init__( + self, parser, parent: ParserRuleContext = None, invokingState: int = -1 + ): super().__init__(parent, invokingState) self.parser = parser def fluiddeclstat(self): - return self.getTypedRuleContext(lfrXParser.FluiddeclstatContext,0) - + return self.getTypedRuleContext(lfrXParser.FluiddeclstatContext, 0) def storagestat(self): - return self.getTypedRuleContext(lfrXParser.StoragestatContext,0) - + return self.getTypedRuleContext(lfrXParser.StoragestatContext, 0) def numvarstat(self): - return self.getTypedRuleContext(lfrXParser.NumvarstatContext,0) - + return self.getTypedRuleContext(lfrXParser.NumvarstatContext, 0) def signalvarstat(self): - return self.getTypedRuleContext(lfrXParser.SignalvarstatContext,0) - + return self.getTypedRuleContext(lfrXParser.SignalvarstatContext, 0) def pumpvarstat(self): - return self.getTypedRuleContext(lfrXParser.PumpvarstatContext,0) - + return self.getTypedRuleContext(lfrXParser.PumpvarstatContext, 0) def getRuleIndex(self): return lfrXParser.RULE_tempvariablesstat - def enterRule(self, listener:ParseTreeListener): - if hasattr( listener, "enterTempvariablesstat" ): + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterTempvariablesstat"): listener.enterTempvariablesstat(self) - def exitRule(self, listener:ParseTreeListener): - if hasattr( listener, "exitTempvariablesstat" ): + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitTempvariablesstat"): listener.exitTempvariablesstat(self) - def accept(self, visitor:ParseTreeVisitor): - if hasattr( visitor, "visitTempvariablesstat" ): + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitTempvariablesstat"): return visitor.visitTempvariablesstat(self) else: return visitor.visitChildren(self) - - - def tempvariablesstat(self): - localctx = lfrXParser.TempvariablesstatContext(self, self._ctx, self.state) self.enterRule(localctx, 68, self.RULE_tempvariablesstat) try: @@ -2853,46 +7719,42 @@ def tempvariablesstat(self): self.exitRule() return localctx - class SignalvarstatContext(ParserRuleContext): - __slots__ = 'parser' + __slots__ = "parser" - def __init__(self, parser, parent:ParserRuleContext=None, invokingState:int=-1): + def __init__( + self, parser, parent: ParserRuleContext = None, invokingState: int = -1 + ): super().__init__(parent, invokingState) self.parser = parser - def declvar(self, i:int=None): + def declvar(self, i: int = None): if i is None: return self.getTypedRuleContexts(lfrXParser.DeclvarContext) else: - return self.getTypedRuleContext(lfrXParser.DeclvarContext,i) - + return self.getTypedRuleContext(lfrXParser.DeclvarContext, i) def getRuleIndex(self): return lfrXParser.RULE_signalvarstat - def enterRule(self, listener:ParseTreeListener): - if hasattr( listener, "enterSignalvarstat" ): + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterSignalvarstat"): listener.enterSignalvarstat(self) - def exitRule(self, listener:ParseTreeListener): - if hasattr( listener, "exitSignalvarstat" ): + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitSignalvarstat"): listener.exitSignalvarstat(self) - def accept(self, visitor:ParseTreeVisitor): - if hasattr( visitor, "visitSignalvarstat" ): + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitSignalvarstat"): return visitor.visitSignalvarstat(self) else: return visitor.visitChildren(self) - - - def signalvarstat(self): - localctx = lfrXParser.SignalvarstatContext(self, self._ctx, self.state) self.enterRule(localctx, 70, self.RULE_signalvarstat) - self._la = 0 # Token type + self._la = 0 # Token type try: self.enterOuterAlt(localctx, 1) self.state = 373 @@ -2902,7 +7764,7 @@ def signalvarstat(self): self.state = 379 self._errHandler.sync(self) _la = self._input.LA(1) - while _la==lfrXParser.T__5: + while _la == lfrXParser.T__5: self.state = 375 self.match(lfrXParser.T__5) self.state = 376 @@ -2919,46 +7781,42 @@ def signalvarstat(self): self.exitRule() return localctx - class FluiddeclstatContext(ParserRuleContext): - __slots__ = 'parser' + __slots__ = "parser" - def __init__(self, parser, parent:ParserRuleContext=None, invokingState:int=-1): + def __init__( + self, parser, parent: ParserRuleContext = None, invokingState: int = -1 + ): super().__init__(parent, invokingState) self.parser = parser - def declvar(self, i:int=None): + def declvar(self, i: int = None): if i is None: return self.getTypedRuleContexts(lfrXParser.DeclvarContext) else: - return self.getTypedRuleContext(lfrXParser.DeclvarContext,i) - + return self.getTypedRuleContext(lfrXParser.DeclvarContext, i) def getRuleIndex(self): return lfrXParser.RULE_fluiddeclstat - def enterRule(self, listener:ParseTreeListener): - if hasattr( listener, "enterFluiddeclstat" ): + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterFluiddeclstat"): listener.enterFluiddeclstat(self) - def exitRule(self, listener:ParseTreeListener): - if hasattr( listener, "exitFluiddeclstat" ): + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitFluiddeclstat"): listener.exitFluiddeclstat(self) - def accept(self, visitor:ParseTreeVisitor): - if hasattr( visitor, "visitFluiddeclstat" ): + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitFluiddeclstat"): return visitor.visitFluiddeclstat(self) else: return visitor.visitChildren(self) - - - def fluiddeclstat(self): - localctx = lfrXParser.FluiddeclstatContext(self, self._ctx, self.state) self.enterRule(localctx, 72, self.RULE_fluiddeclstat) - self._la = 0 # Token type + self._la = 0 # Token type try: self.enterOuterAlt(localctx, 1) self.state = 382 @@ -2968,7 +7826,7 @@ def fluiddeclstat(self): self.state = 388 self._errHandler.sync(self) _la = self._input.LA(1) - while _la==lfrXParser.T__5: + while _la == lfrXParser.T__5: self.state = 384 self.match(lfrXParser.T__5) self.state = 385 @@ -2985,46 +7843,42 @@ def fluiddeclstat(self): self.exitRule() return localctx - class StoragestatContext(ParserRuleContext): - __slots__ = 'parser' + __slots__ = "parser" - def __init__(self, parser, parent:ParserRuleContext=None, invokingState:int=-1): + def __init__( + self, parser, parent: ParserRuleContext = None, invokingState: int = -1 + ): super().__init__(parent, invokingState) self.parser = parser - def declvar(self, i:int=None): + def declvar(self, i: int = None): if i is None: return self.getTypedRuleContexts(lfrXParser.DeclvarContext) else: - return self.getTypedRuleContext(lfrXParser.DeclvarContext,i) - + return self.getTypedRuleContext(lfrXParser.DeclvarContext, i) def getRuleIndex(self): return lfrXParser.RULE_storagestat - def enterRule(self, listener:ParseTreeListener): - if hasattr( listener, "enterStoragestat" ): + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterStoragestat"): listener.enterStoragestat(self) - def exitRule(self, listener:ParseTreeListener): - if hasattr( listener, "exitStoragestat" ): + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitStoragestat"): listener.exitStoragestat(self) - def accept(self, visitor:ParseTreeVisitor): - if hasattr( visitor, "visitStoragestat" ): + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitStoragestat"): return visitor.visitStoragestat(self) else: return visitor.visitChildren(self) - - - def storagestat(self): - localctx = lfrXParser.StoragestatContext(self, self._ctx, self.state) self.enterRule(localctx, 74, self.RULE_storagestat) - self._la = 0 # Token type + self._la = 0 # Token type try: self.enterOuterAlt(localctx, 1) self.state = 391 @@ -3034,7 +7888,7 @@ def storagestat(self): self.state = 397 self._errHandler.sync(self) _la = self._input.LA(1) - while _la==lfrXParser.T__5: + while _la == lfrXParser.T__5: self.state = 393 self.match(lfrXParser.T__5) self.state = 394 @@ -3051,46 +7905,42 @@ def storagestat(self): self.exitRule() return localctx - class PumpvarstatContext(ParserRuleContext): - __slots__ = 'parser' + __slots__ = "parser" - def __init__(self, parser, parent:ParserRuleContext=None, invokingState:int=-1): + def __init__( + self, parser, parent: ParserRuleContext = None, invokingState: int = -1 + ): super().__init__(parent, invokingState) self.parser = parser - def declvar(self, i:int=None): + def declvar(self, i: int = None): if i is None: return self.getTypedRuleContexts(lfrXParser.DeclvarContext) else: - return self.getTypedRuleContext(lfrXParser.DeclvarContext,i) - + return self.getTypedRuleContext(lfrXParser.DeclvarContext, i) def getRuleIndex(self): return lfrXParser.RULE_pumpvarstat - def enterRule(self, listener:ParseTreeListener): - if hasattr( listener, "enterPumpvarstat" ): + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterPumpvarstat"): listener.enterPumpvarstat(self) - def exitRule(self, listener:ParseTreeListener): - if hasattr( listener, "exitPumpvarstat" ): + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitPumpvarstat"): listener.exitPumpvarstat(self) - def accept(self, visitor:ParseTreeVisitor): - if hasattr( visitor, "visitPumpvarstat" ): + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitPumpvarstat"): return visitor.visitPumpvarstat(self) else: return visitor.visitChildren(self) - - - def pumpvarstat(self): - localctx = lfrXParser.PumpvarstatContext(self, self._ctx, self.state) self.enterRule(localctx, 76, self.RULE_pumpvarstat) - self._la = 0 # Token type + self._la = 0 # Token type try: self.enterOuterAlt(localctx, 1) self.state = 400 @@ -3100,7 +7950,7 @@ def pumpvarstat(self): self.state = 406 self._errHandler.sync(self) _la = self._input.LA(1) - while _la==lfrXParser.T__5: + while _la == lfrXParser.T__5: self.state = 402 self.match(lfrXParser.T__5) self.state = 403 @@ -3117,46 +7967,42 @@ def pumpvarstat(self): self.exitRule() return localctx - class NumvarstatContext(ParserRuleContext): - __slots__ = 'parser' + __slots__ = "parser" - def __init__(self, parser, parent:ParserRuleContext=None, invokingState:int=-1): + def __init__( + self, parser, parent: ParserRuleContext = None, invokingState: int = -1 + ): super().__init__(parent, invokingState) self.parser = parser - def literalassignstat(self, i:int=None): + def literalassignstat(self, i: int = None): if i is None: return self.getTypedRuleContexts(lfrXParser.LiteralassignstatContext) else: - return self.getTypedRuleContext(lfrXParser.LiteralassignstatContext,i) - + return self.getTypedRuleContext(lfrXParser.LiteralassignstatContext, i) def getRuleIndex(self): return lfrXParser.RULE_numvarstat - def enterRule(self, listener:ParseTreeListener): - if hasattr( listener, "enterNumvarstat" ): + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterNumvarstat"): listener.enterNumvarstat(self) - def exitRule(self, listener:ParseTreeListener): - if hasattr( listener, "exitNumvarstat" ): + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitNumvarstat"): listener.exitNumvarstat(self) - def accept(self, visitor:ParseTreeVisitor): - if hasattr( visitor, "visitNumvarstat" ): + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitNumvarstat"): return visitor.visitNumvarstat(self) else: return visitor.visitChildren(self) - - - def numvarstat(self): - localctx = lfrXParser.NumvarstatContext(self, self._ctx, self.state) self.enterRule(localctx, 78, self.RULE_numvarstat) - self._la = 0 # Token type + self._la = 0 # Token type try: self.enterOuterAlt(localctx, 1) self.state = 409 @@ -3166,7 +8012,7 @@ def numvarstat(self): self.state = 415 self._errHandler.sync(self) _la = self._input.LA(1) - while _la==lfrXParser.T__5: + while _la == lfrXParser.T__5: self.state = 411 self.match(lfrXParser.T__5) self.state = 412 @@ -3183,48 +8029,42 @@ def numvarstat(self): self.exitRule() return localctx - class AssignstatContext(ParserRuleContext): - __slots__ = 'parser' + __slots__ = "parser" - def __init__(self, parser, parent:ParserRuleContext=None, invokingState:int=-1): + def __init__( + self, parser, parent: ParserRuleContext = None, invokingState: int = -1 + ): super().__init__(parent, invokingState) self.parser = parser def lhs(self): - return self.getTypedRuleContext(lfrXParser.LhsContext,0) - + return self.getTypedRuleContext(lfrXParser.LhsContext, 0) def bracketexpression(self): - return self.getTypedRuleContext(lfrXParser.BracketexpressionContext,0) - + return self.getTypedRuleContext(lfrXParser.BracketexpressionContext, 0) def expression(self): - return self.getTypedRuleContext(lfrXParser.ExpressionContext,0) - + return self.getTypedRuleContext(lfrXParser.ExpressionContext, 0) def getRuleIndex(self): return lfrXParser.RULE_assignstat - def enterRule(self, listener:ParseTreeListener): - if hasattr( listener, "enterAssignstat" ): + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterAssignstat"): listener.enterAssignstat(self) - def exitRule(self, listener:ParseTreeListener): - if hasattr( listener, "exitAssignstat" ): + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitAssignstat"): listener.exitAssignstat(self) - def accept(self, visitor:ParseTreeVisitor): - if hasattr( visitor, "visitAssignstat" ): + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitAssignstat"): return visitor.visitAssignstat(self) else: return visitor.visitChildren(self) - - - def assignstat(self): - localctx = lfrXParser.AssignstatContext(self, self._ctx, self.state) self.enterRule(localctx, 80, self.RULE_assignstat) try: @@ -3237,7 +8077,7 @@ def assignstat(self): self.match(lfrXParser.T__26) self.state = 423 self._errHandler.sync(self) - la_ = self._interp.adaptivePredict(self._input,35,self._ctx) + la_ = self._interp.adaptivePredict(self._input, 35, self._ctx) if la_ == 1: self.state = 421 self.bracketexpression() @@ -3248,7 +8088,6 @@ def assignstat(self): self.expression() pass - except RecognitionException as re: localctx.exception = re self._errHandler.reportError(self, re) @@ -3257,11 +8096,12 @@ def assignstat(self): self.exitRule() return localctx - class LiteralassignstatContext(ParserRuleContext): - __slots__ = 'parser' + __slots__ = "parser" - def __init__(self, parser, parent:ParserRuleContext=None, invokingState:int=-1): + def __init__( + self, parser, parent: ParserRuleContext = None, invokingState: int = -1 + ): super().__init__(parent, invokingState) self.parser = parser @@ -3269,35 +8109,29 @@ def ID(self): return self.getToken(lfrXParser.ID, 0) def bracketexpression(self): - return self.getTypedRuleContext(lfrXParser.BracketexpressionContext,0) - + return self.getTypedRuleContext(lfrXParser.BracketexpressionContext, 0) def expression(self): - return self.getTypedRuleContext(lfrXParser.ExpressionContext,0) - + return self.getTypedRuleContext(lfrXParser.ExpressionContext, 0) def getRuleIndex(self): return lfrXParser.RULE_literalassignstat - def enterRule(self, listener:ParseTreeListener): - if hasattr( listener, "enterLiteralassignstat" ): + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterLiteralassignstat"): listener.enterLiteralassignstat(self) - def exitRule(self, listener:ParseTreeListener): - if hasattr( listener, "exitLiteralassignstat" ): + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitLiteralassignstat"): listener.exitLiteralassignstat(self) - def accept(self, visitor:ParseTreeVisitor): - if hasattr( visitor, "visitLiteralassignstat" ): + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitLiteralassignstat"): return visitor.visitLiteralassignstat(self) else: return visitor.visitChildren(self) - - - def literalassignstat(self): - localctx = lfrXParser.LiteralassignstatContext(self, self._ctx, self.state) self.enterRule(localctx, 82, self.RULE_literalassignstat) try: @@ -3308,7 +8142,7 @@ def literalassignstat(self): self.match(lfrXParser.T__26) self.state = 429 self._errHandler.sync(self) - la_ = self._interp.adaptivePredict(self._input,36,self._ctx) + la_ = self._interp.adaptivePredict(self._input, 36, self._ctx) if la_ == 1: self.state = 427 self.bracketexpression() @@ -3319,7 +8153,6 @@ def literalassignstat(self): self.expression() pass - except RecognitionException as re: localctx.exception = re self._errHandler.reportError(self, re) @@ -3328,57 +8161,66 @@ def literalassignstat(self): self.exitRule() return localctx - class BracketexpressionContext(ParserRuleContext): - __slots__ = 'parser' + __slots__ = "parser" - def __init__(self, parser, parent:ParserRuleContext=None, invokingState:int=-1): + def __init__( + self, parser, parent: ParserRuleContext = None, invokingState: int = -1 + ): super().__init__(parent, invokingState) self.parser = parser def expression(self): - return self.getTypedRuleContext(lfrXParser.ExpressionContext,0) - + return self.getTypedRuleContext(lfrXParser.ExpressionContext, 0) def unary_operator(self): - return self.getTypedRuleContext(lfrXParser.Unary_operatorContext,0) - + return self.getTypedRuleContext(lfrXParser.Unary_operatorContext, 0) def getRuleIndex(self): return lfrXParser.RULE_bracketexpression - def enterRule(self, listener:ParseTreeListener): - if hasattr( listener, "enterBracketexpression" ): + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterBracketexpression"): listener.enterBracketexpression(self) - def exitRule(self, listener:ParseTreeListener): - if hasattr( listener, "exitBracketexpression" ): + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitBracketexpression"): listener.exitBracketexpression(self) - def accept(self, visitor:ParseTreeVisitor): - if hasattr( visitor, "visitBracketexpression" ): + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitBracketexpression"): return visitor.visitBracketexpression(self) else: return visitor.visitChildren(self) - - - def bracketexpression(self): - localctx = lfrXParser.BracketexpressionContext(self, self._ctx, self.state) self.enterRule(localctx, 84, self.RULE_bracketexpression) - self._la = 0 # Token type + self._la = 0 # Token type try: self.enterOuterAlt(localctx, 1) self.state = 432 self._errHandler.sync(self) _la = self._input.LA(1) - if (((_la) & ~0x3f) == 0 and ((1 << _la) & ((1 << lfrXParser.T__40) | (1 << lfrXParser.T__41) | (1 << lfrXParser.T__42) | (1 << lfrXParser.T__43) | (1 << lfrXParser.T__44) | (1 << lfrXParser.T__45) | (1 << lfrXParser.T__46) | (1 << lfrXParser.T__47) | (1 << lfrXParser.T__48) | (1 << lfrXParser.T__49) | (1 << lfrXParser.T__50))) != 0): + if ((_la) & ~0x3F) == 0 and ( + (1 << _la) + & ( + (1 << lfrXParser.T__40) + | (1 << lfrXParser.T__41) + | (1 << lfrXParser.T__42) + | (1 << lfrXParser.T__43) + | (1 << lfrXParser.T__44) + | (1 << lfrXParser.T__45) + | (1 << lfrXParser.T__46) + | (1 << lfrXParser.T__47) + | (1 << lfrXParser.T__48) + | (1 << lfrXParser.T__49) + | (1 << lfrXParser.T__50) + ) + ) != 0: self.state = 431 self.unary_operator() - self.state = 434 self.match(lfrXParser.T__2) self.state = 435 @@ -3393,65 +8235,59 @@ def bracketexpression(self): self.exitRule() return localctx - class ExpressionContext(ParserRuleContext): - __slots__ = 'parser' + __slots__ = "parser" - def __init__(self, parser, parent:ParserRuleContext=None, invokingState:int=-1): + def __init__( + self, parser, parent: ParserRuleContext = None, invokingState: int = -1 + ): super().__init__(parent, invokingState) self.parser = parser - def bracketexpression(self, i:int=None): + def bracketexpression(self, i: int = None): if i is None: return self.getTypedRuleContexts(lfrXParser.BracketexpressionContext) else: - return self.getTypedRuleContext(lfrXParser.BracketexpressionContext,i) - + return self.getTypedRuleContext(lfrXParser.BracketexpressionContext, i) - def expressionterm(self, i:int=None): + def expressionterm(self, i: int = None): if i is None: return self.getTypedRuleContexts(lfrXParser.ExpressiontermContext) else: - return self.getTypedRuleContext(lfrXParser.ExpressiontermContext,i) + return self.getTypedRuleContext(lfrXParser.ExpressiontermContext, i) - - def binary_operator(self, i:int=None): + def binary_operator(self, i: int = None): if i is None: return self.getTypedRuleContexts(lfrXParser.Binary_operatorContext) else: - return self.getTypedRuleContext(lfrXParser.Binary_operatorContext,i) - + return self.getTypedRuleContext(lfrXParser.Binary_operatorContext, i) def getRuleIndex(self): return lfrXParser.RULE_expression - def enterRule(self, listener:ParseTreeListener): - if hasattr( listener, "enterExpression" ): + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterExpression"): listener.enterExpression(self) - def exitRule(self, listener:ParseTreeListener): - if hasattr( listener, "exitExpression" ): + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitExpression"): listener.exitExpression(self) - def accept(self, visitor:ParseTreeVisitor): - if hasattr( visitor, "visitExpression" ): + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitExpression"): return visitor.visitExpression(self) else: return visitor.visitChildren(self) - - - def expression(self): - localctx = lfrXParser.ExpressionContext(self, self._ctx, self.state) self.enterRule(localctx, 86, self.RULE_expression) - self._la = 0 # Token type + self._la = 0 # Token type try: self.enterOuterAlt(localctx, 1) self.state = 440 self._errHandler.sync(self) - la_ = self._interp.adaptivePredict(self._input,38,self._ctx) + la_ = self._interp.adaptivePredict(self._input, 38, self._ctx) if la_ == 1: self.state = 438 self.bracketexpression() @@ -3462,16 +8298,44 @@ def expression(self): self.expressionterm() pass - self.state = 449 self._errHandler.sync(self) _la = self._input.LA(1) - while ((((_la - 19)) & ~0x3f) == 0 and ((1 << (_la - 19)) & ((1 << (lfrXParser.T__18 - 19)) | (1 << (lfrXParser.T__37 - 19)) | (1 << (lfrXParser.T__38 - 19)) | (1 << (lfrXParser.T__39 - 19)) | (1 << (lfrXParser.T__40 - 19)) | (1 << (lfrXParser.T__41 - 19)) | (1 << (lfrXParser.T__44 - 19)) | (1 << (lfrXParser.T__46 - 19)) | (1 << (lfrXParser.T__48 - 19)) | (1 << (lfrXParser.T__49 - 19)) | (1 << (lfrXParser.T__50 - 19)) | (1 << (lfrXParser.T__51 - 19)) | (1 << (lfrXParser.T__52 - 19)) | (1 << (lfrXParser.T__53 - 19)) | (1 << (lfrXParser.T__54 - 19)) | (1 << (lfrXParser.T__55 - 19)) | (1 << (lfrXParser.T__56 - 19)) | (1 << (lfrXParser.T__57 - 19)) | (1 << (lfrXParser.T__58 - 19)) | (1 << (lfrXParser.T__59 - 19)) | (1 << (lfrXParser.T__60 - 19)) | (1 << (lfrXParser.T__61 - 19)) | (1 << (lfrXParser.T__62 - 19)) | (1 << (lfrXParser.T__63 - 19)) | (1 << (lfrXParser.T__64 - 19)))) != 0): + while (((_la - 19)) & ~0x3F) == 0 and ( + (1 << (_la - 19)) + & ( + (1 << (lfrXParser.T__18 - 19)) + | (1 << (lfrXParser.T__37 - 19)) + | (1 << (lfrXParser.T__38 - 19)) + | (1 << (lfrXParser.T__39 - 19)) + | (1 << (lfrXParser.T__40 - 19)) + | (1 << (lfrXParser.T__41 - 19)) + | (1 << (lfrXParser.T__44 - 19)) + | (1 << (lfrXParser.T__46 - 19)) + | (1 << (lfrXParser.T__48 - 19)) + | (1 << (lfrXParser.T__49 - 19)) + | (1 << (lfrXParser.T__50 - 19)) + | (1 << (lfrXParser.T__51 - 19)) + | (1 << (lfrXParser.T__52 - 19)) + | (1 << (lfrXParser.T__53 - 19)) + | (1 << (lfrXParser.T__54 - 19)) + | (1 << (lfrXParser.T__55 - 19)) + | (1 << (lfrXParser.T__56 - 19)) + | (1 << (lfrXParser.T__57 - 19)) + | (1 << (lfrXParser.T__58 - 19)) + | (1 << (lfrXParser.T__59 - 19)) + | (1 << (lfrXParser.T__60 - 19)) + | (1 << (lfrXParser.T__61 - 19)) + | (1 << (lfrXParser.T__62 - 19)) + | (1 << (lfrXParser.T__63 - 19)) + | (1 << (lfrXParser.T__64 - 19)) + ) + ) != 0: self.state = 442 self.binary_operator() self.state = 445 self._errHandler.sync(self) - la_ = self._interp.adaptivePredict(self._input,39,self._ctx) + la_ = self._interp.adaptivePredict(self._input, 39, self._ctx) if la_ == 1: self.state = 443 self.bracketexpression() @@ -3482,7 +8346,6 @@ def expression(self): self.expressionterm() pass - self.state = 451 self._errHandler.sync(self) _la = self._input.LA(1) @@ -3495,69 +8358,97 @@ def expression(self): self.exitRule() return localctx - class ExpressiontermContext(ParserRuleContext): - __slots__ = 'parser' + __slots__ = "parser" - def __init__(self, parser, parent:ParserRuleContext=None, invokingState:int=-1): + def __init__( + self, parser, parent: ParserRuleContext = None, invokingState: int = -1 + ): super().__init__(parent, invokingState) self.parser = parser def variables(self): - return self.getTypedRuleContext(lfrXParser.VariablesContext,0) - + return self.getTypedRuleContext(lfrXParser.VariablesContext, 0) def unary_operator(self): - return self.getTypedRuleContext(lfrXParser.Unary_operatorContext,0) - + return self.getTypedRuleContext(lfrXParser.Unary_operatorContext, 0) def number(self): - return self.getTypedRuleContext(lfrXParser.NumberContext,0) - + return self.getTypedRuleContext(lfrXParser.NumberContext, 0) def getRuleIndex(self): return lfrXParser.RULE_expressionterm - def enterRule(self, listener:ParseTreeListener): - if hasattr( listener, "enterExpressionterm" ): + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterExpressionterm"): listener.enterExpressionterm(self) - def exitRule(self, listener:ParseTreeListener): - if hasattr( listener, "exitExpressionterm" ): + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitExpressionterm"): listener.exitExpressionterm(self) - def accept(self, visitor:ParseTreeVisitor): - if hasattr( visitor, "visitExpressionterm" ): + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitExpressionterm"): return visitor.visitExpressionterm(self) else: return visitor.visitChildren(self) - - - def expressionterm(self): - localctx = lfrXParser.ExpressiontermContext(self, self._ctx, self.state) self.enterRule(localctx, 88, self.RULE_expressionterm) - self._la = 0 # Token type + self._la = 0 # Token type try: self.state = 457 self._errHandler.sync(self) token = self._input.LA(1) - if token in [lfrXParser.T__29, lfrXParser.T__40, lfrXParser.T__41, lfrXParser.T__42, lfrXParser.T__43, lfrXParser.T__44, lfrXParser.T__45, lfrXParser.T__46, lfrXParser.T__47, lfrXParser.T__48, lfrXParser.T__49, lfrXParser.T__50, lfrXParser.ID]: + if token in [ + lfrXParser.T__29, + lfrXParser.T__40, + lfrXParser.T__41, + lfrXParser.T__42, + lfrXParser.T__43, + lfrXParser.T__44, + lfrXParser.T__45, + lfrXParser.T__46, + lfrXParser.T__47, + lfrXParser.T__48, + lfrXParser.T__49, + lfrXParser.T__50, + lfrXParser.ID, + ]: self.enterOuterAlt(localctx, 1) self.state = 453 self._errHandler.sync(self) _la = self._input.LA(1) - if (((_la) & ~0x3f) == 0 and ((1 << _la) & ((1 << lfrXParser.T__40) | (1 << lfrXParser.T__41) | (1 << lfrXParser.T__42) | (1 << lfrXParser.T__43) | (1 << lfrXParser.T__44) | (1 << lfrXParser.T__45) | (1 << lfrXParser.T__46) | (1 << lfrXParser.T__47) | (1 << lfrXParser.T__48) | (1 << lfrXParser.T__49) | (1 << lfrXParser.T__50))) != 0): + if ((_la) & ~0x3F) == 0 and ( + (1 << _la) + & ( + (1 << lfrXParser.T__40) + | (1 << lfrXParser.T__41) + | (1 << lfrXParser.T__42) + | (1 << lfrXParser.T__43) + | (1 << lfrXParser.T__44) + | (1 << lfrXParser.T__45) + | (1 << lfrXParser.T__46) + | (1 << lfrXParser.T__47) + | (1 << lfrXParser.T__48) + | (1 << lfrXParser.T__49) + | (1 << lfrXParser.T__50) + ) + ) != 0: self.state = 452 self.unary_operator() - self.state = 455 self.variables() pass - elif token in [lfrXParser.Real_number, lfrXParser.Decimal_number, lfrXParser.Binary_number, lfrXParser.Octal_number, lfrXParser.Hex_number]: + elif token in [ + lfrXParser.Real_number, + lfrXParser.Decimal_number, + lfrXParser.Binary_number, + lfrXParser.Octal_number, + lfrXParser.Hex_number, + ]: self.enterOuterAlt(localctx, 2) self.state = 456 self.number() @@ -3573,64 +8464,58 @@ def expressionterm(self): self.exitRule() return localctx - class Logiccondition_operandContext(ParserRuleContext): - __slots__ = 'parser' + __slots__ = "parser" - def __init__(self, parser, parent:ParserRuleContext=None, invokingState:int=-1): + def __init__( + self, parser, parent: ParserRuleContext = None, invokingState: int = -1 + ): super().__init__(parent, invokingState) self.parser = parser - def bracketexpression(self, i:int=None): + def bracketexpression(self, i: int = None): if i is None: return self.getTypedRuleContexts(lfrXParser.BracketexpressionContext) else: - return self.getTypedRuleContext(lfrXParser.BracketexpressionContext,i) - + return self.getTypedRuleContext(lfrXParser.BracketexpressionContext, i) - def expressionterm(self, i:int=None): + def expressionterm(self, i: int = None): if i is None: return self.getTypedRuleContexts(lfrXParser.ExpressiontermContext) else: - return self.getTypedRuleContext(lfrXParser.ExpressiontermContext,i) + return self.getTypedRuleContext(lfrXParser.ExpressiontermContext, i) - - def binary_operator(self, i:int=None): + def binary_operator(self, i: int = None): if i is None: return self.getTypedRuleContexts(lfrXParser.Binary_operatorContext) else: - return self.getTypedRuleContext(lfrXParser.Binary_operatorContext,i) - + return self.getTypedRuleContext(lfrXParser.Binary_operatorContext, i) def getRuleIndex(self): return lfrXParser.RULE_logiccondition_operand - def enterRule(self, listener:ParseTreeListener): - if hasattr( listener, "enterLogiccondition_operand" ): + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterLogiccondition_operand"): listener.enterLogiccondition_operand(self) - def exitRule(self, listener:ParseTreeListener): - if hasattr( listener, "exitLogiccondition_operand" ): + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitLogiccondition_operand"): listener.exitLogiccondition_operand(self) - def accept(self, visitor:ParseTreeVisitor): - if hasattr( visitor, "visitLogiccondition_operand" ): + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitLogiccondition_operand"): return visitor.visitLogiccondition_operand(self) else: return visitor.visitChildren(self) - - - def logiccondition_operand(self): - localctx = lfrXParser.Logiccondition_operandContext(self, self._ctx, self.state) self.enterRule(localctx, 90, self.RULE_logiccondition_operand) try: self.enterOuterAlt(localctx, 1) self.state = 461 self._errHandler.sync(self) - la_ = self._interp.adaptivePredict(self._input,43,self._ctx) + la_ = self._interp.adaptivePredict(self._input, 43, self._ctx) if la_ == 1: self.state = 459 self.bracketexpression() @@ -3641,17 +8526,16 @@ def logiccondition_operand(self): self.expressionterm() pass - self.state = 470 self._errHandler.sync(self) - _alt = self._interp.adaptivePredict(self._input,45,self._ctx) - while _alt!=2 and _alt!=ATN.INVALID_ALT_NUMBER: - if _alt==1: + _alt = self._interp.adaptivePredict(self._input, 45, self._ctx) + while _alt != 2 and _alt != ATN.INVALID_ALT_NUMBER: + if _alt == 1: self.state = 463 self.binary_operator() self.state = 466 self._errHandler.sync(self) - la_ = self._interp.adaptivePredict(self._input,44,self._ctx) + la_ = self._interp.adaptivePredict(self._input, 44, self._ctx) if la_ == 1: self.state = 464 self.bracketexpression() @@ -3662,10 +8546,9 @@ def logiccondition_operand(self): self.expressionterm() pass - self.state = 472 self._errHandler.sync(self) - _alt = self._interp.adaptivePredict(self._input,45,self._ctx) + _alt = self._interp.adaptivePredict(self._input, 45, self._ctx) except RecognitionException as re: localctx.exception = re @@ -3675,48 +8558,44 @@ def logiccondition_operand(self): self.exitRule() return localctx - class LogicconditionContext(ParserRuleContext): - __slots__ = 'parser' + __slots__ = "parser" - def __init__(self, parser, parent:ParserRuleContext=None, invokingState:int=-1): + def __init__( + self, parser, parent: ParserRuleContext = None, invokingState: int = -1 + ): super().__init__(parent, invokingState) self.parser = parser def logiccondition_operand(self): - return self.getTypedRuleContext(lfrXParser.Logiccondition_operandContext,0) - + return self.getTypedRuleContext(lfrXParser.Logiccondition_operandContext, 0) def binary_module_path_operator(self): - return self.getTypedRuleContext(lfrXParser.Binary_module_path_operatorContext,0) - + return self.getTypedRuleContext( + lfrXParser.Binary_module_path_operatorContext, 0 + ) def logic_value(self): - return self.getTypedRuleContext(lfrXParser.Logic_valueContext,0) - + return self.getTypedRuleContext(lfrXParser.Logic_valueContext, 0) def getRuleIndex(self): return lfrXParser.RULE_logiccondition - def enterRule(self, listener:ParseTreeListener): - if hasattr( listener, "enterLogiccondition" ): + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterLogiccondition"): listener.enterLogiccondition(self) - def exitRule(self, listener:ParseTreeListener): - if hasattr( listener, "exitLogiccondition" ): + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitLogiccondition"): listener.exitLogiccondition(self) - def accept(self, visitor:ParseTreeVisitor): - if hasattr( visitor, "visitLogiccondition" ): + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitLogiccondition"): return visitor.visitLogiccondition(self) else: return visitor.visitChildren(self) - - - def logiccondition(self): - localctx = lfrXParser.LogicconditionContext(self, self._ctx, self.state) self.enterRule(localctx, 92, self.RULE_logiccondition) try: @@ -3735,40 +8614,36 @@ def logiccondition(self): self.exitRule() return localctx - class Logic_valueContext(ParserRuleContext): - __slots__ = 'parser' + __slots__ = "parser" - def __init__(self, parser, parent:ParserRuleContext=None, invokingState:int=-1): + def __init__( + self, parser, parent: ParserRuleContext = None, invokingState: int = -1 + ): super().__init__(parent, invokingState) self.parser = parser def number(self): - return self.getTypedRuleContext(lfrXParser.NumberContext,0) - + return self.getTypedRuleContext(lfrXParser.NumberContext, 0) def getRuleIndex(self): return lfrXParser.RULE_logic_value - def enterRule(self, listener:ParseTreeListener): - if hasattr( listener, "enterLogic_value" ): + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterLogic_value"): listener.enterLogic_value(self) - def exitRule(self, listener:ParseTreeListener): - if hasattr( listener, "exitLogic_value" ): + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitLogic_value"): listener.exitLogic_value(self) - def accept(self, visitor:ParseTreeVisitor): - if hasattr( visitor, "visitLogic_value" ): + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitLogic_value"): return visitor.visitLogic_value(self) else: return visitor.visitChildren(self) - - - def logic_value(self): - localctx = lfrXParser.Logic_valueContext(self, self._ctx, self.state) self.enterRule(localctx, 94, self.RULE_logic_value) try: @@ -3783,17 +8658,18 @@ def logic_value(self): self.exitRule() return localctx - class VectorContext(ParserRuleContext): - __slots__ = 'parser' + __slots__ = "parser" - def __init__(self, parser, parent:ParserRuleContext=None, invokingState:int=-1): + def __init__( + self, parser, parent: ParserRuleContext = None, invokingState: int = -1 + ): super().__init__(parent, invokingState) self.parser = parser - self.start = None # Token - self.end = None # Token + self.start = None # Token + self.end = None # Token - def Decimal_number(self, i:int=None): + def Decimal_number(self, i: int = None): if i is None: return self.getTokens(lfrXParser.Decimal_number) else: @@ -3802,28 +8678,24 @@ def Decimal_number(self, i:int=None): def getRuleIndex(self): return lfrXParser.RULE_vector - def enterRule(self, listener:ParseTreeListener): - if hasattr( listener, "enterVector" ): + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterVector"): listener.enterVector(self) - def exitRule(self, listener:ParseTreeListener): - if hasattr( listener, "exitVector" ): + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitVector"): listener.exitVector(self) - def accept(self, visitor:ParseTreeVisitor): - if hasattr( visitor, "visitVector" ): + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitVector"): return visitor.visitVector(self) else: return visitor.visitChildren(self) - - - def vector(self): - localctx = lfrXParser.VectorContext(self, self._ctx, self.state) self.enterRule(localctx, 96, self.RULE_vector) - self._la = 0 # Token type + self._la = 0 # Token type try: self.enterOuterAlt(localctx, 1) self.state = 479 @@ -3833,13 +8705,12 @@ def vector(self): self.state = 483 self._errHandler.sync(self) _la = self._input.LA(1) - if _la==lfrXParser.T__16: + if _la == lfrXParser.T__16: self.state = 481 self.match(lfrXParser.T__16) self.state = 482 localctx.end = self.match(lfrXParser.Decimal_number) - self.state = 485 self.match(lfrXParser.T__28) except RecognitionException as re: @@ -3850,44 +8721,39 @@ def vector(self): self.exitRule() return localctx - class VariablesContext(ParserRuleContext): - __slots__ = 'parser' + __slots__ = "parser" - def __init__(self, parser, parent:ParserRuleContext=None, invokingState:int=-1): + def __init__( + self, parser, parent: ParserRuleContext = None, invokingState: int = -1 + ): super().__init__(parent, invokingState) self.parser = parser def vectorvar(self): - return self.getTypedRuleContext(lfrXParser.VectorvarContext,0) - + return self.getTypedRuleContext(lfrXParser.VectorvarContext, 0) def concatenation(self): - return self.getTypedRuleContext(lfrXParser.ConcatenationContext,0) - + return self.getTypedRuleContext(lfrXParser.ConcatenationContext, 0) def getRuleIndex(self): return lfrXParser.RULE_variables - def enterRule(self, listener:ParseTreeListener): - if hasattr( listener, "enterVariables" ): + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterVariables"): listener.enterVariables(self) - def exitRule(self, listener:ParseTreeListener): - if hasattr( listener, "exitVariables" ): + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitVariables"): listener.exitVariables(self) - def accept(self, visitor:ParseTreeVisitor): - if hasattr( visitor, "visitVariables" ): + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitVariables"): return visitor.visitVariables(self) else: return visitor.visitChildren(self) - - - def variables(self): - localctx = lfrXParser.VariablesContext(self, self._ctx, self.state) self.enterRule(localctx, 98, self.RULE_variables) try: @@ -3915,50 +8781,45 @@ def variables(self): self.exitRule() return localctx - class ConcatenationContext(ParserRuleContext): - __slots__ = 'parser' + __slots__ = "parser" - def __init__(self, parser, parent:ParserRuleContext=None, invokingState:int=-1): + def __init__( + self, parser, parent: ParserRuleContext = None, invokingState: int = -1 + ): super().__init__(parent, invokingState) self.parser = parser - def vectorvar(self, i:int=None): + def vectorvar(self, i: int = None): if i is None: return self.getTypedRuleContexts(lfrXParser.VectorvarContext) else: - return self.getTypedRuleContext(lfrXParser.VectorvarContext,i) - + return self.getTypedRuleContext(lfrXParser.VectorvarContext, i) def vector(self): - return self.getTypedRuleContext(lfrXParser.VectorContext,0) - + return self.getTypedRuleContext(lfrXParser.VectorContext, 0) def getRuleIndex(self): return lfrXParser.RULE_concatenation - def enterRule(self, listener:ParseTreeListener): - if hasattr( listener, "enterConcatenation" ): + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterConcatenation"): listener.enterConcatenation(self) - def exitRule(self, listener:ParseTreeListener): - if hasattr( listener, "exitConcatenation" ): + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitConcatenation"): listener.exitConcatenation(self) - def accept(self, visitor:ParseTreeVisitor): - if hasattr( visitor, "visitConcatenation" ): + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitConcatenation"): return visitor.visitConcatenation(self) else: return visitor.visitChildren(self) - - - def concatenation(self): - localctx = lfrXParser.ConcatenationContext(self, self._ctx, self.state) self.enterRule(localctx, 100, self.RULE_concatenation) - self._la = 0 # Token type + self._la = 0 # Token type try: self.enterOuterAlt(localctx, 1) self.state = 491 @@ -3968,7 +8829,7 @@ def concatenation(self): self.state = 497 self._errHandler.sync(self) _la = self._input.LA(1) - while _la==lfrXParser.T__5: + while _la == lfrXParser.T__5: self.state = 493 self.match(lfrXParser.T__5) self.state = 494 @@ -3982,11 +8843,10 @@ def concatenation(self): self.state = 502 self._errHandler.sync(self) _la = self._input.LA(1) - if _la==lfrXParser.T__27: + if _la == lfrXParser.T__27: self.state = 501 self.vector() - except RecognitionException as re: localctx.exception = re self._errHandler.reportError(self, re) @@ -3995,40 +8855,36 @@ def concatenation(self): self.exitRule() return localctx - class LhsContext(ParserRuleContext): - __slots__ = 'parser' + __slots__ = "parser" - def __init__(self, parser, parent:ParserRuleContext=None, invokingState:int=-1): + def __init__( + self, parser, parent: ParserRuleContext = None, invokingState: int = -1 + ): super().__init__(parent, invokingState) self.parser = parser def variables(self): - return self.getTypedRuleContext(lfrXParser.VariablesContext,0) - + return self.getTypedRuleContext(lfrXParser.VariablesContext, 0) def getRuleIndex(self): return lfrXParser.RULE_lhs - def enterRule(self, listener:ParseTreeListener): - if hasattr( listener, "enterLhs" ): + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterLhs"): listener.enterLhs(self) - def exitRule(self, listener:ParseTreeListener): - if hasattr( listener, "exitLhs" ): + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitLhs"): listener.exitLhs(self) - def accept(self, visitor:ParseTreeVisitor): - if hasattr( visitor, "visitLhs" ): + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitLhs"): return visitor.visitLhs(self) else: return visitor.visitChildren(self) - - - def lhs(self): - localctx = lfrXParser.LhsContext(self, self._ctx, self.state) self.enterRule(localctx, 102, self.RULE_lhs) try: @@ -4043,40 +8899,36 @@ def lhs(self): self.exitRule() return localctx - class IoassignstatContext(ParserRuleContext): - __slots__ = 'parser' + __slots__ = "parser" - def __init__(self, parser, parent:ParserRuleContext=None, invokingState:int=-1): + def __init__( + self, parser, parent: ParserRuleContext = None, invokingState: int = -1 + ): super().__init__(parent, invokingState) self.parser = parser def explicitIOBlock(self): - return self.getTypedRuleContext(lfrXParser.ExplicitIOBlockContext,0) - + return self.getTypedRuleContext(lfrXParser.ExplicitIOBlockContext, 0) def getRuleIndex(self): return lfrXParser.RULE_ioassignstat - def enterRule(self, listener:ParseTreeListener): - if hasattr( listener, "enterIoassignstat" ): + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterIoassignstat"): listener.enterIoassignstat(self) - def exitRule(self, listener:ParseTreeListener): - if hasattr( listener, "exitIoassignstat" ): + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitIoassignstat"): listener.exitIoassignstat(self) - def accept(self, visitor:ParseTreeVisitor): - if hasattr( visitor, "visitIoassignstat" ): + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitIoassignstat"): return visitor.visitIoassignstat(self) else: return visitor.visitChildren(self) - - - def ioassignstat(self): - localctx = lfrXParser.IoassignstatContext(self, self._ctx, self.state) self.enterRule(localctx, 104, self.RULE_ioassignstat) try: @@ -4091,48 +8943,46 @@ def ioassignstat(self): self.exitRule() return localctx - class TechnologydirectivesContext(ParserRuleContext): - __slots__ = 'parser' + __slots__ = "parser" - def __init__(self, parser, parent:ParserRuleContext=None, invokingState:int=-1): + def __init__( + self, parser, parent: ParserRuleContext = None, invokingState: int = -1 + ): super().__init__(parent, invokingState) self.parser = parser def performancedirective(self): - return self.getTypedRuleContext(lfrXParser.PerformancedirectiveContext,0) - + return self.getTypedRuleContext(lfrXParser.PerformancedirectiveContext, 0) def technologymappingdirective(self): - return self.getTypedRuleContext(lfrXParser.TechnologymappingdirectiveContext,0) - + return self.getTypedRuleContext( + lfrXParser.TechnologymappingdirectiveContext, 0 + ) def materialmappingdirective(self): - return self.getTypedRuleContext(lfrXParser.MaterialmappingdirectiveContext,0) - + return self.getTypedRuleContext( + lfrXParser.MaterialmappingdirectiveContext, 0 + ) def getRuleIndex(self): return lfrXParser.RULE_technologydirectives - def enterRule(self, listener:ParseTreeListener): - if hasattr( listener, "enterTechnologydirectives" ): + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterTechnologydirectives"): listener.enterTechnologydirectives(self) - def exitRule(self, listener:ParseTreeListener): - if hasattr( listener, "exitTechnologydirectives" ): + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitTechnologydirectives"): listener.exitTechnologydirectives(self) - def accept(self, visitor:ParseTreeVisitor): - if hasattr( visitor, "visitTechnologydirectives" ): + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitTechnologydirectives"): return visitor.visitTechnologydirectives(self) else: return visitor.visitChildren(self) - - - def technologydirectives(self): - localctx = lfrXParser.TechnologydirectivesContext(self, self._ctx, self.state) self.enterRule(localctx, 106, self.RULE_technologydirectives) try: @@ -4165,20 +9015,20 @@ def technologydirectives(self): self.exitRule() return localctx - class TechnologymappingdirectiveContext(ParserRuleContext): - __slots__ = 'parser' + __slots__ = "parser" - def __init__(self, parser, parent:ParserRuleContext=None, invokingState:int=-1): + def __init__( + self, parser, parent: ParserRuleContext = None, invokingState: int = -1 + ): super().__init__(parent, invokingState) self.parser = parser - self.assignmode = None # Token + self.assignmode = None # Token def mappingoperator(self): - return self.getTypedRuleContext(lfrXParser.MappingoperatorContext,0) + return self.getTypedRuleContext(lfrXParser.MappingoperatorContext, 0) - - def ID(self, i:int=None): + def ID(self, i: int = None): if i is None: return self.getTokens(lfrXParser.ID) else: @@ -4187,44 +9037,42 @@ def ID(self, i:int=None): def getRuleIndex(self): return lfrXParser.RULE_technologymappingdirective - def enterRule(self, listener:ParseTreeListener): - if hasattr( listener, "enterTechnologymappingdirective" ): + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterTechnologymappingdirective"): listener.enterTechnologymappingdirective(self) - def exitRule(self, listener:ParseTreeListener): - if hasattr( listener, "exitTechnologymappingdirective" ): + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitTechnologymappingdirective"): listener.exitTechnologymappingdirective(self) - def accept(self, visitor:ParseTreeVisitor): - if hasattr( visitor, "visitTechnologymappingdirective" ): + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitTechnologymappingdirective"): return visitor.visitTechnologymappingdirective(self) else: return visitor.visitChildren(self) - - - def technologymappingdirective(self): - - localctx = lfrXParser.TechnologymappingdirectiveContext(self, self._ctx, self.state) + localctx = lfrXParser.TechnologymappingdirectiveContext( + self, self._ctx, self.state + ) self.enterRule(localctx, 108, self.RULE_technologymappingdirective) - self._la = 0 # Token type + self._la = 0 # Token type try: self.enterOuterAlt(localctx, 1) self.state = 513 self.match(lfrXParser.T__31) self.state = 514 self.match(lfrXParser.T__32) - self.state = 516 + self.state = 516 self._errHandler.sync(self) _la = self._input.LA(1) while True: self.state = 515 self.match(lfrXParser.ID) - self.state = 518 + self.state = 518 self._errHandler.sync(self) _la = self._input.LA(1) - if not (_la==lfrXParser.ID): + if not (_la == lfrXParser.ID): break self.state = 520 @@ -4234,7 +9082,37 @@ def technologymappingdirective(self): self.state = 524 self._errHandler.sync(self) token = self._input.LA(1) - if token in [lfrXParser.T__18, lfrXParser.T__37, lfrXParser.T__38, lfrXParser.T__39, lfrXParser.T__40, lfrXParser.T__41, lfrXParser.T__42, lfrXParser.T__43, lfrXParser.T__44, lfrXParser.T__45, lfrXParser.T__46, lfrXParser.T__47, lfrXParser.T__48, lfrXParser.T__49, lfrXParser.T__50, lfrXParser.T__51, lfrXParser.T__52, lfrXParser.T__53, lfrXParser.T__54, lfrXParser.T__55, lfrXParser.T__56, lfrXParser.T__57, lfrXParser.T__58, lfrXParser.T__59, lfrXParser.T__60, lfrXParser.T__61, lfrXParser.T__62, lfrXParser.T__63, lfrXParser.T__64]: + if token in [ + lfrXParser.T__18, + lfrXParser.T__37, + lfrXParser.T__38, + lfrXParser.T__39, + lfrXParser.T__40, + lfrXParser.T__41, + lfrXParser.T__42, + lfrXParser.T__43, + lfrXParser.T__44, + lfrXParser.T__45, + lfrXParser.T__46, + lfrXParser.T__47, + lfrXParser.T__48, + lfrXParser.T__49, + lfrXParser.T__50, + lfrXParser.T__51, + lfrXParser.T__52, + lfrXParser.T__53, + lfrXParser.T__54, + lfrXParser.T__55, + lfrXParser.T__56, + lfrXParser.T__57, + lfrXParser.T__58, + lfrXParser.T__59, + lfrXParser.T__60, + lfrXParser.T__61, + lfrXParser.T__62, + lfrXParser.T__63, + lfrXParser.T__64, + ]: self.state = 522 self.mappingoperator() pass @@ -4242,7 +9120,20 @@ def technologymappingdirective(self): self.state = 523 localctx.assignmode = self._input.LT(1) _la = self._input.LA(1) - if not((((_la) & ~0x3f) == 0 and ((1 << _la) & ((1 << lfrXParser.T__22) | (1 << lfrXParser.T__23) | (1 << lfrXParser.T__25))) != 0)): + if not ( + ( + ((_la) & ~0x3F) == 0 + and ( + (1 << _la) + & ( + (1 << lfrXParser.T__22) + | (1 << lfrXParser.T__23) + | (1 << lfrXParser.T__25) + ) + ) + != 0 + ) + ): localctx.assignmode = self._errHandler.recoverInline(self) else: self._errHandler.reportMatch(self) @@ -4261,16 +9152,17 @@ def technologymappingdirective(self): self.exitRule() return localctx - class MaterialmappingdirectiveContext(ParserRuleContext): - __slots__ = 'parser' + __slots__ = "parser" - def __init__(self, parser, parent:ParserRuleContext=None, invokingState:int=-1): + def __init__( + self, parser, parent: ParserRuleContext = None, invokingState: int = -1 + ): super().__init__(parent, invokingState) self.parser = parser - self.materialtype = None # Token + self.materialtype = None # Token - def ID(self, i:int=None): + def ID(self, i: int = None): if i is None: return self.getTokens(lfrXParser.ID) else: @@ -4279,26 +9171,24 @@ def ID(self, i:int=None): def getRuleIndex(self): return lfrXParser.RULE_materialmappingdirective - def enterRule(self, listener:ParseTreeListener): - if hasattr( listener, "enterMaterialmappingdirective" ): + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterMaterialmappingdirective"): listener.enterMaterialmappingdirective(self) - def exitRule(self, listener:ParseTreeListener): - if hasattr( listener, "exitMaterialmappingdirective" ): + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitMaterialmappingdirective"): listener.exitMaterialmappingdirective(self) - def accept(self, visitor:ParseTreeVisitor): - if hasattr( visitor, "visitMaterialmappingdirective" ): + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitMaterialmappingdirective"): return visitor.visitMaterialmappingdirective(self) else: return visitor.visitChildren(self) - - - def materialmappingdirective(self): - - localctx = lfrXParser.MaterialmappingdirectiveContext(self, self._ctx, self.state) + localctx = lfrXParser.MaterialmappingdirectiveContext( + self, self._ctx, self.state + ) self.enterRule(localctx, 110, self.RULE_materialmappingdirective) try: self.enterOuterAlt(localctx, 1) @@ -4316,50 +9206,45 @@ def materialmappingdirective(self): self.exitRule() return localctx - class MappingoperatorContext(ParserRuleContext): - __slots__ = 'parser' + __slots__ = "parser" - def __init__(self, parser, parent:ParserRuleContext=None, invokingState:int=-1): + def __init__( + self, parser, parent: ParserRuleContext = None, invokingState: int = -1 + ): super().__init__(parent, invokingState) self.parser = parser def binary_operator(self): - return self.getTypedRuleContext(lfrXParser.Binary_operatorContext,0) - + return self.getTypedRuleContext(lfrXParser.Binary_operatorContext, 0) def unary_operator(self): - return self.getTypedRuleContext(lfrXParser.Unary_operatorContext,0) - + return self.getTypedRuleContext(lfrXParser.Unary_operatorContext, 0) def getRuleIndex(self): return lfrXParser.RULE_mappingoperator - def enterRule(self, listener:ParseTreeListener): - if hasattr( listener, "enterMappingoperator" ): + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterMappingoperator"): listener.enterMappingoperator(self) - def exitRule(self, listener:ParseTreeListener): - if hasattr( listener, "exitMappingoperator" ): + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitMappingoperator"): listener.exitMappingoperator(self) - def accept(self, visitor:ParseTreeVisitor): - if hasattr( visitor, "visitMappingoperator" ): + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitMappingoperator"): return visitor.visitMappingoperator(self) else: return visitor.visitChildren(self) - - - def mappingoperator(self): - localctx = lfrXParser.MappingoperatorContext(self, self._ctx, self.state) self.enterRule(localctx, 112, self.RULE_mappingoperator) try: self.state = 534 self._errHandler.sync(self) - la_ = self._interp.adaptivePredict(self._input,53,self._ctx) + la_ = self._interp.adaptivePredict(self._input, 53, self._ctx) if la_ == 1: self.enterOuterAlt(localctx, 1) self.state = 532 @@ -4372,7 +9257,6 @@ def mappingoperator(self): self.unary_operator() pass - except RecognitionException as re: localctx.exception = re self._errHandler.reportError(self, re) @@ -4381,50 +9265,45 @@ def mappingoperator(self): self.exitRule() return localctx - class PerformancedirectiveContext(ParserRuleContext): - __slots__ = 'parser' + __slots__ = "parser" - def __init__(self, parser, parent:ParserRuleContext=None, invokingState:int=-1): + def __init__( + self, parser, parent: ParserRuleContext = None, invokingState: int = -1 + ): super().__init__(parent, invokingState) self.parser = parser def mappingoperator(self): - return self.getTypedRuleContext(lfrXParser.MappingoperatorContext,0) + return self.getTypedRuleContext(lfrXParser.MappingoperatorContext, 0) - - def constraint(self, i:int=None): + def constraint(self, i: int = None): if i is None: return self.getTypedRuleContexts(lfrXParser.ConstraintContext) else: - return self.getTypedRuleContext(lfrXParser.ConstraintContext,i) - + return self.getTypedRuleContext(lfrXParser.ConstraintContext, i) def getRuleIndex(self): return lfrXParser.RULE_performancedirective - def enterRule(self, listener:ParseTreeListener): - if hasattr( listener, "enterPerformancedirective" ): + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterPerformancedirective"): listener.enterPerformancedirective(self) - def exitRule(self, listener:ParseTreeListener): - if hasattr( listener, "exitPerformancedirective" ): + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitPerformancedirective"): listener.exitPerformancedirective(self) - def accept(self, visitor:ParseTreeVisitor): - if hasattr( visitor, "visitPerformancedirective" ): + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitPerformancedirective"): return visitor.visitPerformancedirective(self) else: return visitor.visitChildren(self) - - - def performancedirective(self): - localctx = lfrXParser.PerformancedirectiveContext(self, self._ctx, self.state) self.enterRule(localctx, 114, self.RULE_performancedirective) - self._la = 0 # Token type + self._la = 0 # Token type try: self.enterOuterAlt(localctx, 1) self.state = 536 @@ -4440,10 +9319,10 @@ def performancedirective(self): self.state = 545 self._errHandler.sync(self) _la = self._input.LA(1) - while _la==lfrXParser.T__35 or _la==lfrXParser.T__36: + while _la == lfrXParser.T__35 or _la == lfrXParser.T__36: self.state = 541 _la = self._input.LA(1) - if not(_la==lfrXParser.T__35 or _la==lfrXParser.T__36): + if not (_la == lfrXParser.T__35 or _la == lfrXParser.T__36): self._errHandler.recoverInline(self) else: self._errHandler.reportMatch(self) @@ -4462,54 +9341,49 @@ def performancedirective(self): self.exitRule() return localctx - class ConstraintContext(ParserRuleContext): - __slots__ = 'parser' + __slots__ = "parser" - def __init__(self, parser, parent:ParserRuleContext=None, invokingState:int=-1): + def __init__( + self, parser, parent: ParserRuleContext = None, invokingState: int = -1 + ): super().__init__(parent, invokingState) self.parser = parser - self.operator = None # Token + self.operator = None # Token def ID(self): return self.getToken(lfrXParser.ID, 0) def number(self): - return self.getTypedRuleContext(lfrXParser.NumberContext,0) - + return self.getTypedRuleContext(lfrXParser.NumberContext, 0) def unit(self): - return self.getTypedRuleContext(lfrXParser.UnitContext,0) - + return self.getTypedRuleContext(lfrXParser.UnitContext, 0) def getRuleIndex(self): return lfrXParser.RULE_constraint - def enterRule(self, listener:ParseTreeListener): - if hasattr( listener, "enterConstraint" ): + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterConstraint"): listener.enterConstraint(self) - def exitRule(self, listener:ParseTreeListener): - if hasattr( listener, "exitConstraint" ): + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitConstraint"): listener.exitConstraint(self) - def accept(self, visitor:ParseTreeVisitor): - if hasattr( visitor, "visitConstraint" ): + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitConstraint"): return visitor.visitConstraint(self) else: return visitor.visitChildren(self) - - - def constraint(self): - localctx = lfrXParser.ConstraintContext(self, self._ctx, self.state) self.enterRule(localctx, 116, self.RULE_constraint) try: self.state = 578 self._errHandler.sync(self) - la_ = self._interp.adaptivePredict(self._input,60,self._ctx) + la_ = self._interp.adaptivePredict(self._input, 60, self._ctx) if la_ == 1: self.enterOuterAlt(localctx, 1) self.state = 548 @@ -4520,12 +9394,11 @@ def constraint(self): self.number() self.state = 552 self._errHandler.sync(self) - la_ = self._interp.adaptivePredict(self._input,55,self._ctx) + la_ = self._interp.adaptivePredict(self._input, 55, self._ctx) if la_ == 1: self.state = 551 self.unit() - pass elif la_ == 2: @@ -4538,12 +9411,11 @@ def constraint(self): self.number() self.state = 558 self._errHandler.sync(self) - la_ = self._interp.adaptivePredict(self._input,56,self._ctx) + la_ = self._interp.adaptivePredict(self._input, 56, self._ctx) if la_ == 1: self.state = 557 self.unit() - pass elif la_ == 3: @@ -4556,12 +9428,11 @@ def constraint(self): self.number() self.state = 564 self._errHandler.sync(self) - la_ = self._interp.adaptivePredict(self._input,57,self._ctx) + la_ = self._interp.adaptivePredict(self._input, 57, self._ctx) if la_ == 1: self.state = 563 self.unit() - pass elif la_ == 4: @@ -4574,12 +9445,11 @@ def constraint(self): self.number() self.state = 570 self._errHandler.sync(self) - la_ = self._interp.adaptivePredict(self._input,58,self._ctx) + la_ = self._interp.adaptivePredict(self._input, 58, self._ctx) if la_ == 1: self.state = 569 self.unit() - pass elif la_ == 5: @@ -4592,15 +9462,13 @@ def constraint(self): self.number() self.state = 576 self._errHandler.sync(self) - la_ = self._interp.adaptivePredict(self._input,59,self._ctx) + la_ = self._interp.adaptivePredict(self._input, 59, self._ctx) if la_ == 1: self.state = 575 self.unit() - pass - except RecognitionException as re: localctx.exception = re self._errHandler.reportError(self, re) @@ -4609,11 +9477,12 @@ def constraint(self): self.exitRule() return localctx - class UnitContext(ParserRuleContext): - __slots__ = 'parser' + __slots__ = "parser" - def __init__(self, parser, parent:ParserRuleContext=None, invokingState:int=-1): + def __init__( + self, parser, parent: ParserRuleContext = None, invokingState: int = -1 + ): super().__init__(parent, invokingState) self.parser = parser @@ -4623,25 +9492,21 @@ def ID(self): def getRuleIndex(self): return lfrXParser.RULE_unit - def enterRule(self, listener:ParseTreeListener): - if hasattr( listener, "enterUnit" ): + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterUnit"): listener.enterUnit(self) - def exitRule(self, listener:ParseTreeListener): - if hasattr( listener, "exitUnit" ): + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitUnit"): listener.exitUnit(self) - def accept(self, visitor:ParseTreeVisitor): - if hasattr( visitor, "visitUnit" ): + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitUnit"): return visitor.visitUnit(self) else: return visitor.visitChildren(self) - - - def unit(self): - localctx = lfrXParser.UnitContext(self, self._ctx, self.state) self.enterRule(localctx, 118, self.RULE_unit) try: @@ -4656,45 +9521,62 @@ def unit(self): self.exitRule() return localctx - class Unary_operatorContext(ParserRuleContext): - __slots__ = 'parser' + __slots__ = "parser" - def __init__(self, parser, parent:ParserRuleContext=None, invokingState:int=-1): + def __init__( + self, parser, parent: ParserRuleContext = None, invokingState: int = -1 + ): super().__init__(parent, invokingState) self.parser = parser - def getRuleIndex(self): return lfrXParser.RULE_unary_operator - def enterRule(self, listener:ParseTreeListener): - if hasattr( listener, "enterUnary_operator" ): + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterUnary_operator"): listener.enterUnary_operator(self) - def exitRule(self, listener:ParseTreeListener): - if hasattr( listener, "exitUnary_operator" ): + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitUnary_operator"): listener.exitUnary_operator(self) - def accept(self, visitor:ParseTreeVisitor): - if hasattr( visitor, "visitUnary_operator" ): + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitUnary_operator"): return visitor.visitUnary_operator(self) else: return visitor.visitChildren(self) - - - def unary_operator(self): - localctx = lfrXParser.Unary_operatorContext(self, self._ctx, self.state) self.enterRule(localctx, 120, self.RULE_unary_operator) - self._la = 0 # Token type + self._la = 0 # Token type try: self.enterOuterAlt(localctx, 1) self.state = 582 _la = self._input.LA(1) - if not((((_la) & ~0x3f) == 0 and ((1 << _la) & ((1 << lfrXParser.T__40) | (1 << lfrXParser.T__41) | (1 << lfrXParser.T__42) | (1 << lfrXParser.T__43) | (1 << lfrXParser.T__44) | (1 << lfrXParser.T__45) | (1 << lfrXParser.T__46) | (1 << lfrXParser.T__47) | (1 << lfrXParser.T__48) | (1 << lfrXParser.T__49) | (1 << lfrXParser.T__50))) != 0)): + if not ( + ( + ((_la) & ~0x3F) == 0 + and ( + (1 << _la) + & ( + (1 << lfrXParser.T__40) + | (1 << lfrXParser.T__41) + | (1 << lfrXParser.T__42) + | (1 << lfrXParser.T__43) + | (1 << lfrXParser.T__44) + | (1 << lfrXParser.T__45) + | (1 << lfrXParser.T__46) + | (1 << lfrXParser.T__47) + | (1 << lfrXParser.T__48) + | (1 << lfrXParser.T__49) + | (1 << lfrXParser.T__50) + ) + ) + != 0 + ) + ): self._errHandler.recoverInline(self) else: self._errHandler.reportMatch(self) @@ -4707,45 +9589,76 @@ def unary_operator(self): self.exitRule() return localctx - class Binary_operatorContext(ParserRuleContext): - __slots__ = 'parser' + __slots__ = "parser" - def __init__(self, parser, parent:ParserRuleContext=None, invokingState:int=-1): + def __init__( + self, parser, parent: ParserRuleContext = None, invokingState: int = -1 + ): super().__init__(parent, invokingState) self.parser = parser - def getRuleIndex(self): return lfrXParser.RULE_binary_operator - def enterRule(self, listener:ParseTreeListener): - if hasattr( listener, "enterBinary_operator" ): + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterBinary_operator"): listener.enterBinary_operator(self) - def exitRule(self, listener:ParseTreeListener): - if hasattr( listener, "exitBinary_operator" ): + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitBinary_operator"): listener.exitBinary_operator(self) - def accept(self, visitor:ParseTreeVisitor): - if hasattr( visitor, "visitBinary_operator" ): + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitBinary_operator"): return visitor.visitBinary_operator(self) else: return visitor.visitChildren(self) - - - def binary_operator(self): - localctx = lfrXParser.Binary_operatorContext(self, self._ctx, self.state) self.enterRule(localctx, 122, self.RULE_binary_operator) - self._la = 0 # Token type + self._la = 0 # Token type try: self.enterOuterAlt(localctx, 1) self.state = 584 _la = self._input.LA(1) - if not(((((_la - 19)) & ~0x3f) == 0 and ((1 << (_la - 19)) & ((1 << (lfrXParser.T__18 - 19)) | (1 << (lfrXParser.T__37 - 19)) | (1 << (lfrXParser.T__38 - 19)) | (1 << (lfrXParser.T__39 - 19)) | (1 << (lfrXParser.T__40 - 19)) | (1 << (lfrXParser.T__41 - 19)) | (1 << (lfrXParser.T__44 - 19)) | (1 << (lfrXParser.T__46 - 19)) | (1 << (lfrXParser.T__48 - 19)) | (1 << (lfrXParser.T__49 - 19)) | (1 << (lfrXParser.T__50 - 19)) | (1 << (lfrXParser.T__51 - 19)) | (1 << (lfrXParser.T__52 - 19)) | (1 << (lfrXParser.T__53 - 19)) | (1 << (lfrXParser.T__54 - 19)) | (1 << (lfrXParser.T__55 - 19)) | (1 << (lfrXParser.T__56 - 19)) | (1 << (lfrXParser.T__57 - 19)) | (1 << (lfrXParser.T__58 - 19)) | (1 << (lfrXParser.T__59 - 19)) | (1 << (lfrXParser.T__60 - 19)) | (1 << (lfrXParser.T__61 - 19)) | (1 << (lfrXParser.T__62 - 19)) | (1 << (lfrXParser.T__63 - 19)) | (1 << (lfrXParser.T__64 - 19)))) != 0)): + if not ( + ( + (((_la - 19)) & ~0x3F) == 0 + and ( + (1 << (_la - 19)) + & ( + (1 << (lfrXParser.T__18 - 19)) + | (1 << (lfrXParser.T__37 - 19)) + | (1 << (lfrXParser.T__38 - 19)) + | (1 << (lfrXParser.T__39 - 19)) + | (1 << (lfrXParser.T__40 - 19)) + | (1 << (lfrXParser.T__41 - 19)) + | (1 << (lfrXParser.T__44 - 19)) + | (1 << (lfrXParser.T__46 - 19)) + | (1 << (lfrXParser.T__48 - 19)) + | (1 << (lfrXParser.T__49 - 19)) + | (1 << (lfrXParser.T__50 - 19)) + | (1 << (lfrXParser.T__51 - 19)) + | (1 << (lfrXParser.T__52 - 19)) + | (1 << (lfrXParser.T__53 - 19)) + | (1 << (lfrXParser.T__54 - 19)) + | (1 << (lfrXParser.T__55 - 19)) + | (1 << (lfrXParser.T__56 - 19)) + | (1 << (lfrXParser.T__57 - 19)) + | (1 << (lfrXParser.T__58 - 19)) + | (1 << (lfrXParser.T__59 - 19)) + | (1 << (lfrXParser.T__60 - 19)) + | (1 << (lfrXParser.T__61 - 19)) + | (1 << (lfrXParser.T__62 - 19)) + | (1 << (lfrXParser.T__63 - 19)) + | (1 << (lfrXParser.T__64 - 19)) + ) + ) + != 0 + ) + ): self._errHandler.recoverInline(self) else: self._errHandler.reportMatch(self) @@ -4758,45 +9671,62 @@ def binary_operator(self): self.exitRule() return localctx - class Unary_module_path_operatorContext(ParserRuleContext): - __slots__ = 'parser' + __slots__ = "parser" - def __init__(self, parser, parent:ParserRuleContext=None, invokingState:int=-1): + def __init__( + self, parser, parent: ParserRuleContext = None, invokingState: int = -1 + ): super().__init__(parent, invokingState) self.parser = parser - def getRuleIndex(self): return lfrXParser.RULE_unary_module_path_operator - def enterRule(self, listener:ParseTreeListener): - if hasattr( listener, "enterUnary_module_path_operator" ): + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterUnary_module_path_operator"): listener.enterUnary_module_path_operator(self) - def exitRule(self, listener:ParseTreeListener): - if hasattr( listener, "exitUnary_module_path_operator" ): + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitUnary_module_path_operator"): listener.exitUnary_module_path_operator(self) - def accept(self, visitor:ParseTreeVisitor): - if hasattr( visitor, "visitUnary_module_path_operator" ): + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitUnary_module_path_operator"): return visitor.visitUnary_module_path_operator(self) else: return visitor.visitChildren(self) - - - def unary_module_path_operator(self): - - localctx = lfrXParser.Unary_module_path_operatorContext(self, self._ctx, self.state) + localctx = lfrXParser.Unary_module_path_operatorContext( + self, self._ctx, self.state + ) self.enterRule(localctx, 124, self.RULE_unary_module_path_operator) - self._la = 0 # Token type + self._la = 0 # Token type try: self.enterOuterAlt(localctx, 1) self.state = 586 _la = self._input.LA(1) - if not((((_la) & ~0x3f) == 0 and ((1 << _la) & ((1 << lfrXParser.T__42) | (1 << lfrXParser.T__43) | (1 << lfrXParser.T__44) | (1 << lfrXParser.T__45) | (1 << lfrXParser.T__46) | (1 << lfrXParser.T__47) | (1 << lfrXParser.T__48) | (1 << lfrXParser.T__49) | (1 << lfrXParser.T__50))) != 0)): + if not ( + ( + ((_la) & ~0x3F) == 0 + and ( + (1 << _la) + & ( + (1 << lfrXParser.T__42) + | (1 << lfrXParser.T__43) + | (1 << lfrXParser.T__44) + | (1 << lfrXParser.T__45) + | (1 << lfrXParser.T__46) + | (1 << lfrXParser.T__47) + | (1 << lfrXParser.T__48) + | (1 << lfrXParser.T__49) + | (1 << lfrXParser.T__50) + ) + ) + != 0 + ) + ): self._errHandler.recoverInline(self) else: self._errHandler.reportMatch(self) @@ -4809,45 +9739,62 @@ def unary_module_path_operator(self): self.exitRule() return localctx - class Binary_module_path_operatorContext(ParserRuleContext): - __slots__ = 'parser' + __slots__ = "parser" - def __init__(self, parser, parent:ParserRuleContext=None, invokingState:int=-1): + def __init__( + self, parser, parent: ParserRuleContext = None, invokingState: int = -1 + ): super().__init__(parent, invokingState) self.parser = parser - def getRuleIndex(self): return lfrXParser.RULE_binary_module_path_operator - def enterRule(self, listener:ParseTreeListener): - if hasattr( listener, "enterBinary_module_path_operator" ): + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterBinary_module_path_operator"): listener.enterBinary_module_path_operator(self) - def exitRule(self, listener:ParseTreeListener): - if hasattr( listener, "exitBinary_module_path_operator" ): + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitBinary_module_path_operator"): listener.exitBinary_module_path_operator(self) - def accept(self, visitor:ParseTreeVisitor): - if hasattr( visitor, "visitBinary_module_path_operator" ): + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitBinary_module_path_operator"): return visitor.visitBinary_module_path_operator(self) else: return visitor.visitChildren(self) - - - def binary_module_path_operator(self): - - localctx = lfrXParser.Binary_module_path_operatorContext(self, self._ctx, self.state) + localctx = lfrXParser.Binary_module_path_operatorContext( + self, self._ctx, self.state + ) self.enterRule(localctx, 126, self.RULE_binary_module_path_operator) - self._la = 0 # Token type + self._la = 0 # Token type try: self.enterOuterAlt(localctx, 1) self.state = 588 _la = self._input.LA(1) - if not((((_la) & ~0x3f) == 0 and ((1 << _la) & ((1 << lfrXParser.T__44) | (1 << lfrXParser.T__46) | (1 << lfrXParser.T__48) | (1 << lfrXParser.T__49) | (1 << lfrXParser.T__50) | (1 << lfrXParser.T__54) | (1 << lfrXParser.T__55) | (1 << lfrXParser.T__58) | (1 << lfrXParser.T__59))) != 0)): + if not ( + ( + ((_la) & ~0x3F) == 0 + and ( + (1 << _la) + & ( + (1 << lfrXParser.T__44) + | (1 << lfrXParser.T__46) + | (1 << lfrXParser.T__48) + | (1 << lfrXParser.T__49) + | (1 << lfrXParser.T__50) + | (1 << lfrXParser.T__54) + | (1 << lfrXParser.T__55) + | (1 << lfrXParser.T__58) + | (1 << lfrXParser.T__59) + ) + ) + != 0 + ) + ): self._errHandler.recoverInline(self) else: self._errHandler.reportMatch(self) @@ -4860,11 +9807,12 @@ def binary_module_path_operator(self): self.exitRule() return localctx - class NumberContext(ParserRuleContext): - __slots__ = 'parser' + __slots__ = "parser" - def __init__(self, parser, parent:ParserRuleContext=None, invokingState:int=-1): + def __init__( + self, parser, parent: ParserRuleContext = None, invokingState: int = -1 + ): super().__init__(parent, invokingState) self.parser = parser @@ -4886,33 +9834,44 @@ def Real_number(self): def getRuleIndex(self): return lfrXParser.RULE_number - def enterRule(self, listener:ParseTreeListener): - if hasattr( listener, "enterNumber" ): + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterNumber"): listener.enterNumber(self) - def exitRule(self, listener:ParseTreeListener): - if hasattr( listener, "exitNumber" ): + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitNumber"): listener.exitNumber(self) - def accept(self, visitor:ParseTreeVisitor): - if hasattr( visitor, "visitNumber" ): + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitNumber"): return visitor.visitNumber(self) else: return visitor.visitChildren(self) - - - def number(self): - localctx = lfrXParser.NumberContext(self, self._ctx, self.state) self.enterRule(localctx, 128, self.RULE_number) - self._la = 0 # Token type + self._la = 0 # Token type try: self.enterOuterAlt(localctx, 1) self.state = 590 _la = self._input.LA(1) - if not(((((_la - 71)) & ~0x3f) == 0 and ((1 << (_la - 71)) & ((1 << (lfrXParser.Real_number - 71)) | (1 << (lfrXParser.Decimal_number - 71)) | (1 << (lfrXParser.Binary_number - 71)) | (1 << (lfrXParser.Octal_number - 71)) | (1 << (lfrXParser.Hex_number - 71)))) != 0)): + if not ( + ( + (((_la - 71)) & ~0x3F) == 0 + and ( + (1 << (_la - 71)) + & ( + (1 << (lfrXParser.Real_number - 71)) + | (1 << (lfrXParser.Decimal_number - 71)) + | (1 << (lfrXParser.Binary_number - 71)) + | (1 << (lfrXParser.Octal_number - 71)) + | (1 << (lfrXParser.Hex_number - 71)) + ) + ) + != 0 + ) + ): self._errHandler.recoverInline(self) else: self._errHandler.reportMatch(self) @@ -4924,8 +9883,3 @@ def number(self): finally: self.exitRule() return localctx - - - - - diff --git a/lfr/antlrgen/lfr/lfrXVisitor.py b/lfr/antlrgen/lfr/lfrXVisitor.py index 6297a96..0a9b03d 100755 --- a/lfr/antlrgen/lfr/lfrXVisitor.py +++ b/lfr/antlrgen/lfr/lfrXVisitor.py @@ -1,5 +1,6 @@ -# Generated from ./lfrX.g4 by ANTLR 4.9.1 +# Generated from ./lfrX.g4 by ANTLR 4.10.1 from antlr4 import * + if __name__ is not None and "." in __name__: from .lfrXParser import lfrXParser else: @@ -7,332 +8,283 @@ # This class defines a complete generic visitor for a parse tree produced by lfrXParser. -class lfrXVisitor(ParseTreeVisitor): +class lfrXVisitor(ParseTreeVisitor): # Visit a parse tree produced by lfrXParser#skeleton. - def visitSkeleton(self, ctx:lfrXParser.SkeletonContext): + def visitSkeleton(self, ctx: lfrXParser.SkeletonContext): return self.visitChildren(ctx) - # Visit a parse tree produced by lfrXParser#module. - def visitModule(self, ctx:lfrXParser.ModuleContext): + def visitModule(self, ctx: lfrXParser.ModuleContext): return self.visitChildren(ctx) - # Visit a parse tree produced by lfrXParser#moduledefinition. - def visitModuledefinition(self, ctx:lfrXParser.ModuledefinitionContext): + def visitModuledefinition(self, ctx: lfrXParser.ModuledefinitionContext): return self.visitChildren(ctx) - # Visit a parse tree produced by lfrXParser#body. - def visitBody(self, ctx:lfrXParser.BodyContext): + def visitBody(self, ctx: lfrXParser.BodyContext): return self.visitChildren(ctx) - # Visit a parse tree produced by lfrXParser#ioblock. - def visitIoblock(self, ctx:lfrXParser.IoblockContext): + def visitIoblock(self, ctx: lfrXParser.IoblockContext): return self.visitChildren(ctx) - # Visit a parse tree produced by lfrXParser#vectorvar. - def visitVectorvar(self, ctx:lfrXParser.VectorvarContext): + def visitVectorvar(self, ctx: lfrXParser.VectorvarContext): return self.visitChildren(ctx) - # Visit a parse tree produced by lfrXParser#explicitIOBlock. - def visitExplicitIOBlock(self, ctx:lfrXParser.ExplicitIOBlockContext): + def visitExplicitIOBlock(self, ctx: lfrXParser.ExplicitIOBlockContext): return self.visitChildren(ctx) - # Visit a parse tree produced by lfrXParser#declvar. - def visitDeclvar(self, ctx:lfrXParser.DeclvarContext): + def visitDeclvar(self, ctx: lfrXParser.DeclvarContext): return self.visitChildren(ctx) - # Visit a parse tree produced by lfrXParser#distributionBlock. - def visitDistributionBlock(self, ctx:lfrXParser.DistributionBlockContext): + def visitDistributionBlock(self, ctx: lfrXParser.DistributionBlockContext): return self.visitChildren(ctx) - # Visit a parse tree produced by lfrXParser#distributionBody. - def visitDistributionBody(self, ctx:lfrXParser.DistributionBodyContext): + def visitDistributionBody(self, ctx: lfrXParser.DistributionBodyContext): return self.visitChildren(ctx) - # Visit a parse tree produced by lfrXParser#distributeBodyStat. - def visitDistributeBodyStat(self, ctx:lfrXParser.DistributeBodyStatContext): + def visitDistributeBodyStat(self, ctx: lfrXParser.DistributeBodyStatContext): return self.visitChildren(ctx) - # Visit a parse tree produced by lfrXParser#ifElseBlock. - def visitIfElseBlock(self, ctx:lfrXParser.IfElseBlockContext): + def visitIfElseBlock(self, ctx: lfrXParser.IfElseBlockContext): return self.visitChildren(ctx) - # Visit a parse tree produced by lfrXParser#ifBlock. - def visitIfBlock(self, ctx:lfrXParser.IfBlockContext): + def visitIfBlock(self, ctx: lfrXParser.IfBlockContext): return self.visitChildren(ctx) - # Visit a parse tree produced by lfrXParser#elseBlock. - def visitElseBlock(self, ctx:lfrXParser.ElseBlockContext): + def visitElseBlock(self, ctx: lfrXParser.ElseBlockContext): return self.visitChildren(ctx) - # Visit a parse tree produced by lfrXParser#elseIfBlock. - def visitElseIfBlock(self, ctx:lfrXParser.ElseIfBlockContext): + def visitElseIfBlock(self, ctx: lfrXParser.ElseIfBlockContext): return self.visitChildren(ctx) - # Visit a parse tree produced by lfrXParser#distributeCondition. - def visitDistributeCondition(self, ctx:lfrXParser.DistributeConditionContext): + def visitDistributeCondition(self, ctx: lfrXParser.DistributeConditionContext): return self.visitChildren(ctx) - # Visit a parse tree produced by lfrXParser#statementBlock. - def visitStatementBlock(self, ctx:lfrXParser.StatementBlockContext): + def visitStatementBlock(self, ctx: lfrXParser.StatementBlockContext): return self.visitChildren(ctx) - # Visit a parse tree produced by lfrXParser#caseBlock. - def visitCaseBlock(self, ctx:lfrXParser.CaseBlockContext): + def visitCaseBlock(self, ctx: lfrXParser.CaseBlockContext): return self.visitChildren(ctx) - # Visit a parse tree produced by lfrXParser#caseBlockHeader. - def visitCaseBlockHeader(self, ctx:lfrXParser.CaseBlockHeaderContext): + def visitCaseBlockHeader(self, ctx: lfrXParser.CaseBlockHeaderContext): return self.visitChildren(ctx) - # Visit a parse tree produced by lfrXParser#casestat. - def visitCasestat(self, ctx:lfrXParser.CasestatContext): + def visitCasestat(self, ctx: lfrXParser.CasestatContext): return self.visitChildren(ctx) - # Visit a parse tree produced by lfrXParser#defaultCaseStat. - def visitDefaultCaseStat(self, ctx:lfrXParser.DefaultCaseStatContext): + def visitDefaultCaseStat(self, ctx: lfrXParser.DefaultCaseStatContext): return self.visitChildren(ctx) - # Visit a parse tree produced by lfrXParser#distvalue. - def visitDistvalue(self, ctx:lfrXParser.DistvalueContext): + def visitDistvalue(self, ctx: lfrXParser.DistvalueContext): return self.visitChildren(ctx) - # Visit a parse tree produced by lfrXParser#distributionassignstat. - def visitDistributionassignstat(self, ctx:lfrXParser.DistributionassignstatContext): + def visitDistributionassignstat( + self, ctx: lfrXParser.DistributionassignstatContext + ): return self.visitChildren(ctx) - # Visit a parse tree produced by lfrXParser#sensitivitylist. - def visitSensitivitylist(self, ctx:lfrXParser.SensitivitylistContext): + def visitSensitivitylist(self, ctx: lfrXParser.SensitivitylistContext): return self.visitChildren(ctx) - # Visit a parse tree produced by lfrXParser#signal. - def visitSignal(self, ctx:lfrXParser.SignalContext): + def visitSignal(self, ctx: lfrXParser.SignalContext): return self.visitChildren(ctx) - # Visit a parse tree produced by lfrXParser#statements. - def visitStatements(self, ctx:lfrXParser.StatementsContext): + def visitStatements(self, ctx: lfrXParser.StatementsContext): return self.visitChildren(ctx) - # Visit a parse tree produced by lfrXParser#statement. - def visitStatement(self, ctx:lfrXParser.StatementContext): + def visitStatement(self, ctx: lfrXParser.StatementContext): return self.visitChildren(ctx) - # Visit a parse tree produced by lfrXParser#moduleinstantiationstat. - def visitModuleinstantiationstat(self, ctx:lfrXParser.ModuleinstantiationstatContext): + def visitModuleinstantiationstat( + self, ctx: lfrXParser.ModuleinstantiationstatContext + ): return self.visitChildren(ctx) - # Visit a parse tree produced by lfrXParser#instanceioblock. - def visitInstanceioblock(self, ctx:lfrXParser.InstanceioblockContext): + def visitInstanceioblock(self, ctx: lfrXParser.InstanceioblockContext): return self.visitChildren(ctx) - # Visit a parse tree produced by lfrXParser#orderedioblock. - def visitOrderedioblock(self, ctx:lfrXParser.OrderedioblockContext): + def visitOrderedioblock(self, ctx: lfrXParser.OrderedioblockContext): return self.visitChildren(ctx) - # Visit a parse tree produced by lfrXParser#unorderedioblock. - def visitUnorderedioblock(self, ctx:lfrXParser.UnorderedioblockContext): + def visitUnorderedioblock(self, ctx: lfrXParser.UnorderedioblockContext): return self.visitChildren(ctx) - # Visit a parse tree produced by lfrXParser#explicitinstanceiomapping. - def visitExplicitinstanceiomapping(self, ctx:lfrXParser.ExplicitinstanceiomappingContext): + def visitExplicitinstanceiomapping( + self, ctx: lfrXParser.ExplicitinstanceiomappingContext + ): return self.visitChildren(ctx) - # Visit a parse tree produced by lfrXParser#instancename. - def visitInstancename(self, ctx:lfrXParser.InstancenameContext): + def visitInstancename(self, ctx: lfrXParser.InstancenameContext): return self.visitChildren(ctx) - # Visit a parse tree produced by lfrXParser#moduletype. - def visitModuletype(self, ctx:lfrXParser.ModuletypeContext): + def visitModuletype(self, ctx: lfrXParser.ModuletypeContext): return self.visitChildren(ctx) - # Visit a parse tree produced by lfrXParser#tempvariablesstat. - def visitTempvariablesstat(self, ctx:lfrXParser.TempvariablesstatContext): + def visitTempvariablesstat(self, ctx: lfrXParser.TempvariablesstatContext): return self.visitChildren(ctx) - # Visit a parse tree produced by lfrXParser#signalvarstat. - def visitSignalvarstat(self, ctx:lfrXParser.SignalvarstatContext): + def visitSignalvarstat(self, ctx: lfrXParser.SignalvarstatContext): return self.visitChildren(ctx) - # Visit a parse tree produced by lfrXParser#fluiddeclstat. - def visitFluiddeclstat(self, ctx:lfrXParser.FluiddeclstatContext): + def visitFluiddeclstat(self, ctx: lfrXParser.FluiddeclstatContext): return self.visitChildren(ctx) - # Visit a parse tree produced by lfrXParser#storagestat. - def visitStoragestat(self, ctx:lfrXParser.StoragestatContext): + def visitStoragestat(self, ctx: lfrXParser.StoragestatContext): return self.visitChildren(ctx) - # Visit a parse tree produced by lfrXParser#pumpvarstat. - def visitPumpvarstat(self, ctx:lfrXParser.PumpvarstatContext): + def visitPumpvarstat(self, ctx: lfrXParser.PumpvarstatContext): return self.visitChildren(ctx) - # Visit a parse tree produced by lfrXParser#numvarstat. - def visitNumvarstat(self, ctx:lfrXParser.NumvarstatContext): + def visitNumvarstat(self, ctx: lfrXParser.NumvarstatContext): return self.visitChildren(ctx) - # Visit a parse tree produced by lfrXParser#assignstat. - def visitAssignstat(self, ctx:lfrXParser.AssignstatContext): + def visitAssignstat(self, ctx: lfrXParser.AssignstatContext): return self.visitChildren(ctx) - # Visit a parse tree produced by lfrXParser#literalassignstat. - def visitLiteralassignstat(self, ctx:lfrXParser.LiteralassignstatContext): + def visitLiteralassignstat(self, ctx: lfrXParser.LiteralassignstatContext): return self.visitChildren(ctx) - # Visit a parse tree produced by lfrXParser#bracketexpression. - def visitBracketexpression(self, ctx:lfrXParser.BracketexpressionContext): + def visitBracketexpression(self, ctx: lfrXParser.BracketexpressionContext): return self.visitChildren(ctx) - # Visit a parse tree produced by lfrXParser#expression. - def visitExpression(self, ctx:lfrXParser.ExpressionContext): + def visitExpression(self, ctx: lfrXParser.ExpressionContext): return self.visitChildren(ctx) - # Visit a parse tree produced by lfrXParser#expressionterm. - def visitExpressionterm(self, ctx:lfrXParser.ExpressiontermContext): + def visitExpressionterm(self, ctx: lfrXParser.ExpressiontermContext): return self.visitChildren(ctx) - # Visit a parse tree produced by lfrXParser#logiccondition_operand. - def visitLogiccondition_operand(self, ctx:lfrXParser.Logiccondition_operandContext): + def visitLogiccondition_operand( + self, ctx: lfrXParser.Logiccondition_operandContext + ): return self.visitChildren(ctx) - # Visit a parse tree produced by lfrXParser#logiccondition. - def visitLogiccondition(self, ctx:lfrXParser.LogicconditionContext): + def visitLogiccondition(self, ctx: lfrXParser.LogicconditionContext): return self.visitChildren(ctx) - # Visit a parse tree produced by lfrXParser#logic_value. - def visitLogic_value(self, ctx:lfrXParser.Logic_valueContext): + def visitLogic_value(self, ctx: lfrXParser.Logic_valueContext): return self.visitChildren(ctx) - # Visit a parse tree produced by lfrXParser#vector. - def visitVector(self, ctx:lfrXParser.VectorContext): + def visitVector(self, ctx: lfrXParser.VectorContext): return self.visitChildren(ctx) - # Visit a parse tree produced by lfrXParser#variables. - def visitVariables(self, ctx:lfrXParser.VariablesContext): + def visitVariables(self, ctx: lfrXParser.VariablesContext): return self.visitChildren(ctx) - # Visit a parse tree produced by lfrXParser#concatenation. - def visitConcatenation(self, ctx:lfrXParser.ConcatenationContext): + def visitConcatenation(self, ctx: lfrXParser.ConcatenationContext): return self.visitChildren(ctx) - # Visit a parse tree produced by lfrXParser#lhs. - def visitLhs(self, ctx:lfrXParser.LhsContext): + def visitLhs(self, ctx: lfrXParser.LhsContext): return self.visitChildren(ctx) - # Visit a parse tree produced by lfrXParser#ioassignstat. - def visitIoassignstat(self, ctx:lfrXParser.IoassignstatContext): + def visitIoassignstat(self, ctx: lfrXParser.IoassignstatContext): return self.visitChildren(ctx) - # Visit a parse tree produced by lfrXParser#technologydirectives. - def visitTechnologydirectives(self, ctx:lfrXParser.TechnologydirectivesContext): + def visitTechnologydirectives(self, ctx: lfrXParser.TechnologydirectivesContext): return self.visitChildren(ctx) - # Visit a parse tree produced by lfrXParser#technologymappingdirective. - def visitTechnologymappingdirective(self, ctx:lfrXParser.TechnologymappingdirectiveContext): + def visitTechnologymappingdirective( + self, ctx: lfrXParser.TechnologymappingdirectiveContext + ): return self.visitChildren(ctx) - # Visit a parse tree produced by lfrXParser#materialmappingdirective. - def visitMaterialmappingdirective(self, ctx:lfrXParser.MaterialmappingdirectiveContext): + def visitMaterialmappingdirective( + self, ctx: lfrXParser.MaterialmappingdirectiveContext + ): return self.visitChildren(ctx) - # Visit a parse tree produced by lfrXParser#mappingoperator. - def visitMappingoperator(self, ctx:lfrXParser.MappingoperatorContext): + def visitMappingoperator(self, ctx: lfrXParser.MappingoperatorContext): return self.visitChildren(ctx) - # Visit a parse tree produced by lfrXParser#performancedirective. - def visitPerformancedirective(self, ctx:lfrXParser.PerformancedirectiveContext): + def visitPerformancedirective(self, ctx: lfrXParser.PerformancedirectiveContext): return self.visitChildren(ctx) - # Visit a parse tree produced by lfrXParser#constraint. - def visitConstraint(self, ctx:lfrXParser.ConstraintContext): + def visitConstraint(self, ctx: lfrXParser.ConstraintContext): return self.visitChildren(ctx) - # Visit a parse tree produced by lfrXParser#unit. - def visitUnit(self, ctx:lfrXParser.UnitContext): + def visitUnit(self, ctx: lfrXParser.UnitContext): return self.visitChildren(ctx) - # Visit a parse tree produced by lfrXParser#unary_operator. - def visitUnary_operator(self, ctx:lfrXParser.Unary_operatorContext): + def visitUnary_operator(self, ctx: lfrXParser.Unary_operatorContext): return self.visitChildren(ctx) - # Visit a parse tree produced by lfrXParser#binary_operator. - def visitBinary_operator(self, ctx:lfrXParser.Binary_operatorContext): + def visitBinary_operator(self, ctx: lfrXParser.Binary_operatorContext): return self.visitChildren(ctx) - # Visit a parse tree produced by lfrXParser#unary_module_path_operator. - def visitUnary_module_path_operator(self, ctx:lfrXParser.Unary_module_path_operatorContext): + def visitUnary_module_path_operator( + self, ctx: lfrXParser.Unary_module_path_operatorContext + ): return self.visitChildren(ctx) - # Visit a parse tree produced by lfrXParser#binary_module_path_operator. - def visitBinary_module_path_operator(self, ctx:lfrXParser.Binary_module_path_operatorContext): + def visitBinary_module_path_operator( + self, ctx: lfrXParser.Binary_module_path_operatorContext + ): return self.visitChildren(ctx) - # Visit a parse tree produced by lfrXParser#number. - def visitNumber(self, ctx:lfrXParser.NumberContext): + def visitNumber(self, ctx: lfrXParser.NumberContext): return self.visitChildren(ctx) - -del lfrXParser \ No newline at end of file +del lfrXParser diff --git a/lfr/antlrgen/reggie/reggie.interp b/lfr/antlrgen/reggie/reggie.interp index 125373a..e0d67b3 100644 --- a/lfr/antlrgen/reggie/reggie.interp +++ b/lfr/antlrgen/reggie/reggie.interp @@ -62,4 +62,4 @@ edge atn: -[3, 24715, 42794, 33075, 47597, 16764, 15335, 30598, 22884, 3, 21, 152, 4, 2, 9, 2, 4, 3, 9, 3, 4, 4, 9, 4, 4, 5, 9, 5, 4, 6, 9, 6, 4, 7, 9, 7, 4, 8, 9, 8, 4, 9, 9, 9, 4, 10, 9, 10, 4, 11, 9, 11, 4, 12, 9, 12, 4, 13, 9, 13, 4, 14, 9, 14, 4, 15, 9, 15, 4, 16, 9, 16, 4, 17, 9, 17, 3, 2, 3, 2, 3, 2, 3, 2, 7, 2, 39, 10, 2, 12, 2, 14, 2, 42, 11, 2, 3, 2, 3, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 5, 3, 52, 10, 3, 3, 4, 3, 4, 3, 4, 5, 4, 57, 10, 4, 3, 5, 3, 5, 3, 5, 5, 5, 62, 10, 5, 3, 6, 3, 6, 3, 6, 3, 6, 3, 6, 3, 6, 3, 6, 7, 6, 71, 10, 6, 12, 6, 14, 6, 74, 11, 6, 3, 6, 3, 6, 3, 7, 3, 7, 3, 7, 5, 7, 81, 10, 7, 3, 7, 5, 7, 84, 10, 7, 3, 7, 7, 7, 87, 10, 7, 12, 7, 14, 7, 90, 11, 7, 3, 8, 3, 8, 3, 8, 3, 8, 7, 8, 96, 10, 8, 12, 8, 14, 8, 99, 11, 8, 3, 8, 3, 8, 3, 9, 3, 9, 3, 9, 5, 9, 106, 10, 9, 3, 10, 3, 10, 3, 10, 3, 10, 3, 11, 3, 11, 3, 12, 3, 12, 3, 13, 3, 13, 3, 14, 3, 14, 3, 14, 7, 14, 121, 10, 14, 12, 14, 14, 14, 124, 11, 14, 3, 14, 3, 14, 3, 14, 3, 14, 7, 14, 130, 10, 14, 12, 14, 14, 14, 133, 11, 14, 3, 14, 3, 14, 5, 14, 137, 10, 14, 3, 15, 3, 15, 3, 16, 3, 16, 3, 16, 3, 16, 7, 16, 145, 10, 16, 12, 16, 14, 16, 148, 11, 16, 3, 17, 3, 17, 3, 17, 2, 2, 18, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 2, 4, 3, 2, 16, 17, 3, 2, 14, 15, 2, 152, 2, 34, 3, 2, 2, 2, 4, 51, 3, 2, 2, 2, 6, 56, 3, 2, 2, 2, 8, 61, 3, 2, 2, 2, 10, 63, 3, 2, 2, 2, 12, 77, 3, 2, 2, 2, 14, 91, 3, 2, 2, 2, 16, 105, 3, 2, 2, 2, 18, 107, 3, 2, 2, 2, 20, 111, 3, 2, 2, 2, 22, 113, 3, 2, 2, 2, 24, 115, 3, 2, 2, 2, 26, 136, 3, 2, 2, 2, 28, 138, 3, 2, 2, 2, 30, 140, 3, 2, 2, 2, 32, 149, 3, 2, 2, 2, 34, 35, 7, 3, 2, 2, 35, 40, 5, 4, 3, 2, 36, 37, 7, 4, 2, 2, 37, 39, 5, 4, 3, 2, 38, 36, 3, 2, 2, 2, 39, 42, 3, 2, 2, 2, 40, 38, 3, 2, 2, 2, 40, 41, 3, 2, 2, 2, 41, 43, 3, 2, 2, 2, 42, 40, 3, 2, 2, 2, 43, 44, 7, 5, 2, 2, 44, 3, 3, 2, 2, 2, 45, 52, 5, 8, 5, 2, 46, 47, 7, 6, 2, 2, 47, 48, 5, 8, 5, 2, 48, 49, 7, 7, 2, 2, 49, 50, 5, 6, 4, 2, 50, 52, 3, 2, 2, 2, 51, 45, 3, 2, 2, 2, 51, 46, 3, 2, 2, 2, 52, 5, 3, 2, 2, 2, 53, 57, 5, 18, 10, 2, 54, 57, 5, 22, 12, 2, 55, 57, 5, 20, 11, 2, 56, 53, 3, 2, 2, 2, 56, 54, 3, 2, 2, 2, 56, 55, 3, 2, 2, 2, 57, 7, 3, 2, 2, 2, 58, 62, 5, 10, 6, 2, 59, 62, 5, 30, 16, 2, 60, 62, 5, 12, 7, 2, 61, 58, 3, 2, 2, 2, 61, 59, 3, 2, 2, 2, 61, 60, 3, 2, 2, 2, 62, 9, 3, 2, 2, 2, 63, 64, 7, 16, 2, 2, 64, 65, 7, 8, 2, 2, 65, 66, 3, 2, 2, 2, 66, 67, 7, 3, 2, 2, 67, 72, 5, 30, 16, 2, 68, 69, 7, 4, 2, 2, 69, 71, 5, 30, 16, 2, 70, 68, 3, 2, 2, 2, 71, 74, 3, 2, 2, 2, 72, 70, 3, 2, 2, 2, 72, 73, 3, 2, 2, 2, 73, 75, 3, 2, 2, 2, 74, 72, 3, 2, 2, 2, 75, 76, 7, 5, 2, 2, 76, 11, 3, 2, 2, 2, 77, 80, 5, 24, 13, 2, 78, 79, 7, 8, 2, 2, 79, 81, 5, 26, 14, 2, 80, 78, 3, 2, 2, 2, 80, 81, 3, 2, 2, 2, 81, 83, 3, 2, 2, 2, 82, 84, 5, 14, 8, 2, 83, 82, 3, 2, 2, 2, 83, 84, 3, 2, 2, 2, 84, 88, 3, 2, 2, 2, 85, 87, 5, 16, 9, 2, 86, 85, 3, 2, 2, 2, 87, 90, 3, 2, 2, 2, 88, 86, 3, 2, 2, 2, 88, 89, 3, 2, 2, 2, 89, 13, 3, 2, 2, 2, 90, 88, 3, 2, 2, 2, 91, 92, 7, 3, 2, 2, 92, 97, 7, 21, 2, 2, 93, 94, 7, 4, 2, 2, 94, 96, 7, 21, 2, 2, 95, 93, 3, 2, 2, 2, 96, 99, 3, 2, 2, 2, 97, 95, 3, 2, 2, 2, 97, 98, 3, 2, 2, 2, 98, 100, 3, 2, 2, 2, 99, 97, 3, 2, 2, 2, 100, 101, 7, 5, 2, 2, 101, 15, 3, 2, 2, 2, 102, 106, 5, 18, 10, 2, 103, 106, 5, 20, 11, 2, 104, 106, 5, 22, 12, 2, 105, 102, 3, 2, 2, 2, 105, 103, 3, 2, 2, 2, 105, 104, 3, 2, 2, 2, 106, 17, 3, 2, 2, 2, 107, 108, 7, 9, 2, 2, 108, 109, 7, 20, 2, 2, 109, 110, 7, 10, 2, 2, 110, 19, 3, 2, 2, 2, 111, 112, 7, 11, 2, 2, 112, 21, 3, 2, 2, 2, 113, 114, 7, 12, 2, 2, 114, 23, 3, 2, 2, 2, 115, 116, 9, 2, 2, 2, 116, 25, 3, 2, 2, 2, 117, 122, 5, 28, 15, 2, 118, 119, 7, 13, 2, 2, 119, 121, 5, 28, 15, 2, 120, 118, 3, 2, 2, 2, 121, 124, 3, 2, 2, 2, 122, 120, 3, 2, 2, 2, 122, 123, 3, 2, 2, 2, 123, 137, 3, 2, 2, 2, 124, 122, 3, 2, 2, 2, 125, 126, 7, 6, 2, 2, 126, 131, 5, 28, 15, 2, 127, 128, 7, 13, 2, 2, 128, 130, 5, 28, 15, 2, 129, 127, 3, 2, 2, 2, 130, 133, 3, 2, 2, 2, 131, 129, 3, 2, 2, 2, 131, 132, 3, 2, 2, 2, 132, 134, 3, 2, 2, 2, 133, 131, 3, 2, 2, 2, 134, 135, 7, 7, 2, 2, 135, 137, 3, 2, 2, 2, 136, 117, 3, 2, 2, 2, 136, 125, 3, 2, 2, 2, 137, 27, 3, 2, 2, 2, 138, 139, 7, 16, 2, 2, 139, 29, 3, 2, 2, 2, 140, 146, 5, 12, 7, 2, 141, 142, 5, 32, 17, 2, 142, 143, 5, 12, 7, 2, 143, 145, 3, 2, 2, 2, 144, 141, 3, 2, 2, 2, 145, 148, 3, 2, 2, 2, 146, 144, 3, 2, 2, 2, 146, 147, 3, 2, 2, 2, 147, 31, 3, 2, 2, 2, 148, 146, 3, 2, 2, 2, 149, 150, 9, 3, 2, 2, 150, 33, 3, 2, 2, 2, 16, 40, 51, 56, 61, 72, 80, 83, 88, 97, 105, 122, 131, 136, 146] \ No newline at end of file +[4, 1, 19, 150, 2, 0, 7, 0, 2, 1, 7, 1, 2, 2, 7, 2, 2, 3, 7, 3, 2, 4, 7, 4, 2, 5, 7, 5, 2, 6, 7, 6, 2, 7, 7, 7, 2, 8, 7, 8, 2, 9, 7, 9, 2, 10, 7, 10, 2, 11, 7, 11, 2, 12, 7, 12, 2, 13, 7, 13, 2, 14, 7, 14, 2, 15, 7, 15, 1, 0, 1, 0, 1, 0, 1, 0, 5, 0, 37, 8, 0, 10, 0, 12, 0, 40, 9, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 1, 50, 8, 1, 1, 2, 1, 2, 1, 2, 3, 2, 55, 8, 2, 1, 3, 1, 3, 1, 3, 3, 3, 60, 8, 3, 1, 4, 1, 4, 1, 4, 1, 4, 1, 4, 1, 4, 1, 4, 5, 4, 69, 8, 4, 10, 4, 12, 4, 72, 9, 4, 1, 4, 1, 4, 1, 5, 1, 5, 1, 5, 3, 5, 79, 8, 5, 1, 5, 3, 5, 82, 8, 5, 1, 5, 5, 5, 85, 8, 5, 10, 5, 12, 5, 88, 9, 5, 1, 6, 1, 6, 1, 6, 1, 6, 5, 6, 94, 8, 6, 10, 6, 12, 6, 97, 9, 6, 1, 6, 1, 6, 1, 7, 1, 7, 1, 7, 3, 7, 104, 8, 7, 1, 8, 1, 8, 1, 8, 1, 8, 1, 9, 1, 9, 1, 10, 1, 10, 1, 11, 1, 11, 1, 12, 1, 12, 1, 12, 5, 12, 119, 8, 12, 10, 12, 12, 12, 122, 9, 12, 1, 12, 1, 12, 1, 12, 1, 12, 5, 12, 128, 8, 12, 10, 12, 12, 12, 131, 9, 12, 1, 12, 1, 12, 3, 12, 135, 8, 12, 1, 13, 1, 13, 1, 14, 1, 14, 1, 14, 1, 14, 5, 14, 143, 8, 14, 10, 14, 12, 14, 146, 9, 14, 1, 15, 1, 15, 1, 15, 0, 0, 16, 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 0, 2, 1, 0, 14, 15, 1, 0, 12, 13, 150, 0, 32, 1, 0, 0, 0, 2, 49, 1, 0, 0, 0, 4, 54, 1, 0, 0, 0, 6, 59, 1, 0, 0, 0, 8, 61, 1, 0, 0, 0, 10, 75, 1, 0, 0, 0, 12, 89, 1, 0, 0, 0, 14, 103, 1, 0, 0, 0, 16, 105, 1, 0, 0, 0, 18, 109, 1, 0, 0, 0, 20, 111, 1, 0, 0, 0, 22, 113, 1, 0, 0, 0, 24, 134, 1, 0, 0, 0, 26, 136, 1, 0, 0, 0, 28, 138, 1, 0, 0, 0, 30, 147, 1, 0, 0, 0, 32, 33, 5, 1, 0, 0, 33, 38, 3, 2, 1, 0, 34, 35, 5, 2, 0, 0, 35, 37, 3, 2, 1, 0, 36, 34, 1, 0, 0, 0, 37, 40, 1, 0, 0, 0, 38, 36, 1, 0, 0, 0, 38, 39, 1, 0, 0, 0, 39, 41, 1, 0, 0, 0, 40, 38, 1, 0, 0, 0, 41, 42, 5, 3, 0, 0, 42, 1, 1, 0, 0, 0, 43, 50, 3, 6, 3, 0, 44, 45, 5, 4, 0, 0, 45, 46, 3, 6, 3, 0, 46, 47, 5, 5, 0, 0, 47, 48, 3, 4, 2, 0, 48, 50, 1, 0, 0, 0, 49, 43, 1, 0, 0, 0, 49, 44, 1, 0, 0, 0, 50, 3, 1, 0, 0, 0, 51, 55, 3, 16, 8, 0, 52, 55, 3, 20, 10, 0, 53, 55, 3, 18, 9, 0, 54, 51, 1, 0, 0, 0, 54, 52, 1, 0, 0, 0, 54, 53, 1, 0, 0, 0, 55, 5, 1, 0, 0, 0, 56, 60, 3, 8, 4, 0, 57, 60, 3, 28, 14, 0, 58, 60, 3, 10, 5, 0, 59, 56, 1, 0, 0, 0, 59, 57, 1, 0, 0, 0, 59, 58, 1, 0, 0, 0, 60, 7, 1, 0, 0, 0, 61, 62, 5, 14, 0, 0, 62, 63, 5, 6, 0, 0, 63, 64, 1, 0, 0, 0, 64, 65, 5, 1, 0, 0, 65, 70, 3, 28, 14, 0, 66, 67, 5, 2, 0, 0, 67, 69, 3, 28, 14, 0, 68, 66, 1, 0, 0, 0, 69, 72, 1, 0, 0, 0, 70, 68, 1, 0, 0, 0, 70, 71, 1, 0, 0, 0, 71, 73, 1, 0, 0, 0, 72, 70, 1, 0, 0, 0, 73, 74, 5, 3, 0, 0, 74, 9, 1, 0, 0, 0, 75, 78, 3, 22, 11, 0, 76, 77, 5, 6, 0, 0, 77, 79, 3, 24, 12, 0, 78, 76, 1, 0, 0, 0, 78, 79, 1, 0, 0, 0, 79, 81, 1, 0, 0, 0, 80, 82, 3, 12, 6, 0, 81, 80, 1, 0, 0, 0, 81, 82, 1, 0, 0, 0, 82, 86, 1, 0, 0, 0, 83, 85, 3, 14, 7, 0, 84, 83, 1, 0, 0, 0, 85, 88, 1, 0, 0, 0, 86, 84, 1, 0, 0, 0, 86, 87, 1, 0, 0, 0, 87, 11, 1, 0, 0, 0, 88, 86, 1, 0, 0, 0, 89, 90, 5, 1, 0, 0, 90, 95, 5, 19, 0, 0, 91, 92, 5, 2, 0, 0, 92, 94, 5, 19, 0, 0, 93, 91, 1, 0, 0, 0, 94, 97, 1, 0, 0, 0, 95, 93, 1, 0, 0, 0, 95, 96, 1, 0, 0, 0, 96, 98, 1, 0, 0, 0, 97, 95, 1, 0, 0, 0, 98, 99, 5, 3, 0, 0, 99, 13, 1, 0, 0, 0, 100, 104, 3, 16, 8, 0, 101, 104, 3, 18, 9, 0, 102, 104, 3, 20, 10, 0, 103, 100, 1, 0, 0, 0, 103, 101, 1, 0, 0, 0, 103, 102, 1, 0, 0, 0, 104, 15, 1, 0, 0, 0, 105, 106, 5, 7, 0, 0, 106, 107, 5, 18, 0, 0, 107, 108, 5, 8, 0, 0, 108, 17, 1, 0, 0, 0, 109, 110, 5, 9, 0, 0, 110, 19, 1, 0, 0, 0, 111, 112, 5, 10, 0, 0, 112, 21, 1, 0, 0, 0, 113, 114, 7, 0, 0, 0, 114, 23, 1, 0, 0, 0, 115, 120, 3, 26, 13, 0, 116, 117, 5, 11, 0, 0, 117, 119, 3, 26, 13, 0, 118, 116, 1, 0, 0, 0, 119, 122, 1, 0, 0, 0, 120, 118, 1, 0, 0, 0, 120, 121, 1, 0, 0, 0, 121, 135, 1, 0, 0, 0, 122, 120, 1, 0, 0, 0, 123, 124, 5, 4, 0, 0, 124, 129, 3, 26, 13, 0, 125, 126, 5, 11, 0, 0, 126, 128, 3, 26, 13, 0, 127, 125, 1, 0, 0, 0, 128, 131, 1, 0, 0, 0, 129, 127, 1, 0, 0, 0, 129, 130, 1, 0, 0, 0, 130, 132, 1, 0, 0, 0, 131, 129, 1, 0, 0, 0, 132, 133, 5, 5, 0, 0, 133, 135, 1, 0, 0, 0, 134, 115, 1, 0, 0, 0, 134, 123, 1, 0, 0, 0, 135, 25, 1, 0, 0, 0, 136, 137, 5, 14, 0, 0, 137, 27, 1, 0, 0, 0, 138, 144, 3, 10, 5, 0, 139, 140, 3, 30, 15, 0, 140, 141, 3, 10, 5, 0, 141, 143, 1, 0, 0, 0, 142, 139, 1, 0, 0, 0, 143, 146, 1, 0, 0, 0, 144, 142, 1, 0, 0, 0, 144, 145, 1, 0, 0, 0, 145, 29, 1, 0, 0, 0, 146, 144, 1, 0, 0, 0, 147, 148, 7, 1, 0, 0, 148, 31, 1, 0, 0, 0, 14, 38, 49, 54, 59, 70, 78, 81, 86, 95, 103, 120, 129, 134, 144] \ No newline at end of file diff --git a/lfr/antlrgen/reggie/reggieLexer.interp b/lfr/antlrgen/reggie/reggieLexer.interp index e00acfc..4c01e37 100644 --- a/lfr/antlrgen/reggie/reggieLexer.interp +++ b/lfr/antlrgen/reggie/reggieLexer.interp @@ -75,4 +75,4 @@ mode names: DEFAULT_MODE atn: -[3, 24715, 42794, 33075, 47597, 16764, 15335, 30598, 22884, 2, 21, 131, 8, 1, 4, 2, 9, 2, 4, 3, 9, 3, 4, 4, 9, 4, 4, 5, 9, 5, 4, 6, 9, 6, 4, 7, 9, 7, 4, 8, 9, 8, 4, 9, 9, 9, 4, 10, 9, 10, 4, 11, 9, 11, 4, 12, 9, 12, 4, 13, 9, 13, 4, 14, 9, 14, 4, 15, 9, 15, 4, 16, 9, 16, 4, 17, 9, 17, 4, 18, 9, 18, 4, 19, 9, 19, 4, 20, 9, 20, 4, 21, 9, 21, 4, 22, 9, 22, 4, 23, 9, 23, 4, 24, 9, 24, 3, 2, 3, 2, 3, 3, 3, 3, 3, 4, 3, 4, 3, 5, 3, 5, 3, 6, 3, 6, 3, 7, 3, 7, 3, 8, 3, 8, 3, 9, 3, 9, 3, 10, 3, 10, 3, 11, 3, 11, 3, 12, 3, 12, 3, 13, 3, 13, 3, 13, 3, 14, 3, 14, 3, 14, 3, 14, 3, 15, 3, 15, 7, 15, 81, 10, 15, 12, 15, 14, 15, 84, 11, 15, 3, 16, 3, 16, 3, 17, 6, 17, 89, 10, 17, 13, 17, 14, 17, 90, 3, 17, 3, 17, 3, 18, 6, 18, 96, 10, 18, 13, 18, 14, 18, 97, 3, 18, 3, 18, 3, 19, 6, 19, 103, 10, 19, 13, 19, 14, 19, 104, 3, 20, 3, 20, 3, 20, 7, 20, 110, 10, 20, 12, 20, 14, 20, 113, 11, 20, 3, 20, 3, 20, 3, 21, 3, 21, 3, 22, 3, 22, 3, 22, 3, 22, 3, 22, 3, 22, 3, 23, 3, 23, 3, 23, 5, 23, 128, 10, 23, 3, 24, 3, 24, 2, 2, 25, 3, 3, 5, 4, 7, 5, 9, 6, 11, 7, 13, 8, 15, 9, 17, 10, 19, 11, 21, 12, 23, 13, 25, 14, 27, 15, 29, 16, 31, 17, 33, 18, 35, 19, 37, 20, 39, 21, 41, 2, 43, 2, 45, 2, 47, 2, 3, 2, 10, 5, 2, 67, 92, 97, 97, 99, 124, 6, 2, 50, 59, 67, 92, 97, 97, 99, 124, 4, 2, 11, 11, 34, 34, 4, 2, 12, 12, 15, 15, 3, 2, 50, 59, 5, 2, 50, 59, 67, 72, 99, 104, 10, 2, 36, 36, 49, 49, 94, 94, 100, 100, 104, 104, 112, 112, 116, 116, 118, 118, 5, 2, 2, 33, 36, 36, 94, 94, 2, 133, 2, 3, 3, 2, 2, 2, 2, 5, 3, 2, 2, 2, 2, 7, 3, 2, 2, 2, 2, 9, 3, 2, 2, 2, 2, 11, 3, 2, 2, 2, 2, 13, 3, 2, 2, 2, 2, 15, 3, 2, 2, 2, 2, 17, 3, 2, 2, 2, 2, 19, 3, 2, 2, 2, 2, 21, 3, 2, 2, 2, 2, 23, 3, 2, 2, 2, 2, 25, 3, 2, 2, 2, 2, 27, 3, 2, 2, 2, 2, 29, 3, 2, 2, 2, 2, 31, 3, 2, 2, 2, 2, 33, 3, 2, 2, 2, 2, 35, 3, 2, 2, 2, 2, 37, 3, 2, 2, 2, 2, 39, 3, 2, 2, 2, 3, 49, 3, 2, 2, 2, 5, 51, 3, 2, 2, 2, 7, 53, 3, 2, 2, 2, 9, 55, 3, 2, 2, 2, 11, 57, 3, 2, 2, 2, 13, 59, 3, 2, 2, 2, 15, 61, 3, 2, 2, 2, 17, 63, 3, 2, 2, 2, 19, 65, 3, 2, 2, 2, 21, 67, 3, 2, 2, 2, 23, 69, 3, 2, 2, 2, 25, 71, 3, 2, 2, 2, 27, 74, 3, 2, 2, 2, 29, 78, 3, 2, 2, 2, 31, 85, 3, 2, 2, 2, 33, 88, 3, 2, 2, 2, 35, 95, 3, 2, 2, 2, 37, 102, 3, 2, 2, 2, 39, 106, 3, 2, 2, 2, 41, 116, 3, 2, 2, 2, 43, 118, 3, 2, 2, 2, 45, 124, 3, 2, 2, 2, 47, 129, 3, 2, 2, 2, 49, 50, 7, 125, 2, 2, 50, 4, 3, 2, 2, 2, 51, 52, 7, 46, 2, 2, 52, 6, 3, 2, 2, 2, 53, 54, 7, 127, 2, 2, 54, 8, 3, 2, 2, 2, 55, 56, 7, 42, 2, 2, 56, 10, 3, 2, 2, 2, 57, 58, 7, 43, 2, 2, 58, 12, 3, 2, 2, 2, 59, 60, 7, 60, 2, 2, 60, 14, 3, 2, 2, 2, 61, 62, 7, 93, 2, 2, 62, 16, 3, 2, 2, 2, 63, 64, 7, 95, 2, 2, 64, 18, 3, 2, 2, 2, 65, 66, 7, 44, 2, 2, 66, 20, 3, 2, 2, 2, 67, 68, 7, 45, 2, 2, 68, 22, 3, 2, 2, 2, 69, 70, 7, 126, 2, 2, 70, 24, 3, 2, 2, 2, 71, 72, 7, 47, 2, 2, 72, 73, 7, 64, 2, 2, 73, 26, 3, 2, 2, 2, 74, 75, 7, 62, 2, 2, 75, 76, 7, 47, 2, 2, 76, 77, 7, 64, 2, 2, 77, 28, 3, 2, 2, 2, 78, 82, 9, 2, 2, 2, 79, 81, 9, 3, 2, 2, 80, 79, 3, 2, 2, 2, 81, 84, 3, 2, 2, 2, 82, 80, 3, 2, 2, 2, 82, 83, 3, 2, 2, 2, 83, 30, 3, 2, 2, 2, 84, 82, 3, 2, 2, 2, 85, 86, 7, 65, 2, 2, 86, 32, 3, 2, 2, 2, 87, 89, 9, 4, 2, 2, 88, 87, 3, 2, 2, 2, 89, 90, 3, 2, 2, 2, 90, 88, 3, 2, 2, 2, 90, 91, 3, 2, 2, 2, 91, 92, 3, 2, 2, 2, 92, 93, 8, 17, 2, 2, 93, 34, 3, 2, 2, 2, 94, 96, 9, 5, 2, 2, 95, 94, 3, 2, 2, 2, 96, 97, 3, 2, 2, 2, 97, 95, 3, 2, 2, 2, 97, 98, 3, 2, 2, 2, 98, 99, 3, 2, 2, 2, 99, 100, 8, 18, 2, 2, 100, 36, 3, 2, 2, 2, 101, 103, 9, 6, 2, 2, 102, 101, 3, 2, 2, 2, 103, 104, 3, 2, 2, 2, 104, 102, 3, 2, 2, 2, 104, 105, 3, 2, 2, 2, 105, 38, 3, 2, 2, 2, 106, 111, 7, 36, 2, 2, 107, 110, 5, 45, 23, 2, 108, 110, 5, 47, 24, 2, 109, 107, 3, 2, 2, 2, 109, 108, 3, 2, 2, 2, 110, 113, 3, 2, 2, 2, 111, 109, 3, 2, 2, 2, 111, 112, 3, 2, 2, 2, 112, 114, 3, 2, 2, 2, 113, 111, 3, 2, 2, 2, 114, 115, 7, 36, 2, 2, 115, 40, 3, 2, 2, 2, 116, 117, 9, 7, 2, 2, 117, 42, 3, 2, 2, 2, 118, 119, 7, 119, 2, 2, 119, 120, 5, 41, 21, 2, 120, 121, 5, 41, 21, 2, 121, 122, 5, 41, 21, 2, 122, 123, 5, 41, 21, 2, 123, 44, 3, 2, 2, 2, 124, 127, 7, 94, 2, 2, 125, 128, 9, 8, 2, 2, 126, 128, 5, 43, 22, 2, 127, 125, 3, 2, 2, 2, 127, 126, 3, 2, 2, 2, 128, 46, 3, 2, 2, 2, 129, 130, 10, 9, 2, 2, 130, 48, 3, 2, 2, 2, 10, 2, 82, 90, 97, 104, 109, 111, 127, 3, 8, 2, 2] \ No newline at end of file +[4, 0, 19, 129, 6, -1, 2, 0, 7, 0, 2, 1, 7, 1, 2, 2, 7, 2, 2, 3, 7, 3, 2, 4, 7, 4, 2, 5, 7, 5, 2, 6, 7, 6, 2, 7, 7, 7, 2, 8, 7, 8, 2, 9, 7, 9, 2, 10, 7, 10, 2, 11, 7, 11, 2, 12, 7, 12, 2, 13, 7, 13, 2, 14, 7, 14, 2, 15, 7, 15, 2, 16, 7, 16, 2, 17, 7, 17, 2, 18, 7, 18, 2, 19, 7, 19, 2, 20, 7, 20, 2, 21, 7, 21, 2, 22, 7, 22, 1, 0, 1, 0, 1, 1, 1, 1, 1, 2, 1, 2, 1, 3, 1, 3, 1, 4, 1, 4, 1, 5, 1, 5, 1, 6, 1, 6, 1, 7, 1, 7, 1, 8, 1, 8, 1, 9, 1, 9, 1, 10, 1, 10, 1, 11, 1, 11, 1, 11, 1, 12, 1, 12, 1, 12, 1, 12, 1, 13, 1, 13, 5, 13, 79, 8, 13, 10, 13, 12, 13, 82, 9, 13, 1, 14, 1, 14, 1, 15, 4, 15, 87, 8, 15, 11, 15, 12, 15, 88, 1, 15, 1, 15, 1, 16, 4, 16, 94, 8, 16, 11, 16, 12, 16, 95, 1, 16, 1, 16, 1, 17, 4, 17, 101, 8, 17, 11, 17, 12, 17, 102, 1, 18, 1, 18, 1, 18, 5, 18, 108, 8, 18, 10, 18, 12, 18, 111, 9, 18, 1, 18, 1, 18, 1, 19, 1, 19, 1, 20, 1, 20, 1, 20, 1, 20, 1, 20, 1, 20, 1, 21, 1, 21, 1, 21, 3, 21, 126, 8, 21, 1, 22, 1, 22, 0, 0, 23, 1, 1, 3, 2, 5, 3, 7, 4, 9, 5, 11, 6, 13, 7, 15, 8, 17, 9, 19, 10, 21, 11, 23, 12, 25, 13, 27, 14, 29, 15, 31, 16, 33, 17, 35, 18, 37, 19, 39, 0, 41, 0, 43, 0, 45, 0, 1, 0, 8, 3, 0, 65, 90, 95, 95, 97, 122, 4, 0, 48, 57, 65, 90, 95, 95, 97, 122, 2, 0, 9, 9, 32, 32, 2, 0, 10, 10, 13, 13, 1, 0, 48, 57, 3, 0, 48, 57, 65, 70, 97, 102, 8, 0, 34, 34, 47, 47, 92, 92, 98, 98, 102, 102, 110, 110, 114, 114, 116, 116, 3, 0, 0, 31, 34, 34, 92, 92, 131, 0, 1, 1, 0, 0, 0, 0, 3, 1, 0, 0, 0, 0, 5, 1, 0, 0, 0, 0, 7, 1, 0, 0, 0, 0, 9, 1, 0, 0, 0, 0, 11, 1, 0, 0, 0, 0, 13, 1, 0, 0, 0, 0, 15, 1, 0, 0, 0, 0, 17, 1, 0, 0, 0, 0, 19, 1, 0, 0, 0, 0, 21, 1, 0, 0, 0, 0, 23, 1, 0, 0, 0, 0, 25, 1, 0, 0, 0, 0, 27, 1, 0, 0, 0, 0, 29, 1, 0, 0, 0, 0, 31, 1, 0, 0, 0, 0, 33, 1, 0, 0, 0, 0, 35, 1, 0, 0, 0, 0, 37, 1, 0, 0, 0, 1, 47, 1, 0, 0, 0, 3, 49, 1, 0, 0, 0, 5, 51, 1, 0, 0, 0, 7, 53, 1, 0, 0, 0, 9, 55, 1, 0, 0, 0, 11, 57, 1, 0, 0, 0, 13, 59, 1, 0, 0, 0, 15, 61, 1, 0, 0, 0, 17, 63, 1, 0, 0, 0, 19, 65, 1, 0, 0, 0, 21, 67, 1, 0, 0, 0, 23, 69, 1, 0, 0, 0, 25, 72, 1, 0, 0, 0, 27, 76, 1, 0, 0, 0, 29, 83, 1, 0, 0, 0, 31, 86, 1, 0, 0, 0, 33, 93, 1, 0, 0, 0, 35, 100, 1, 0, 0, 0, 37, 104, 1, 0, 0, 0, 39, 114, 1, 0, 0, 0, 41, 116, 1, 0, 0, 0, 43, 122, 1, 0, 0, 0, 45, 127, 1, 0, 0, 0, 47, 48, 5, 123, 0, 0, 48, 2, 1, 0, 0, 0, 49, 50, 5, 44, 0, 0, 50, 4, 1, 0, 0, 0, 51, 52, 5, 125, 0, 0, 52, 6, 1, 0, 0, 0, 53, 54, 5, 40, 0, 0, 54, 8, 1, 0, 0, 0, 55, 56, 5, 41, 0, 0, 56, 10, 1, 0, 0, 0, 57, 58, 5, 58, 0, 0, 58, 12, 1, 0, 0, 0, 59, 60, 5, 91, 0, 0, 60, 14, 1, 0, 0, 0, 61, 62, 5, 93, 0, 0, 62, 16, 1, 0, 0, 0, 63, 64, 5, 42, 0, 0, 64, 18, 1, 0, 0, 0, 65, 66, 5, 43, 0, 0, 66, 20, 1, 0, 0, 0, 67, 68, 5, 124, 0, 0, 68, 22, 1, 0, 0, 0, 69, 70, 5, 45, 0, 0, 70, 71, 5, 62, 0, 0, 71, 24, 1, 0, 0, 0, 72, 73, 5, 60, 0, 0, 73, 74, 5, 45, 0, 0, 74, 75, 5, 62, 0, 0, 75, 26, 1, 0, 0, 0, 76, 80, 7, 0, 0, 0, 77, 79, 7, 1, 0, 0, 78, 77, 1, 0, 0, 0, 79, 82, 1, 0, 0, 0, 80, 78, 1, 0, 0, 0, 80, 81, 1, 0, 0, 0, 81, 28, 1, 0, 0, 0, 82, 80, 1, 0, 0, 0, 83, 84, 5, 63, 0, 0, 84, 30, 1, 0, 0, 0, 85, 87, 7, 2, 0, 0, 86, 85, 1, 0, 0, 0, 87, 88, 1, 0, 0, 0, 88, 86, 1, 0, 0, 0, 88, 89, 1, 0, 0, 0, 89, 90, 1, 0, 0, 0, 90, 91, 6, 15, 0, 0, 91, 32, 1, 0, 0, 0, 92, 94, 7, 3, 0, 0, 93, 92, 1, 0, 0, 0, 94, 95, 1, 0, 0, 0, 95, 93, 1, 0, 0, 0, 95, 96, 1, 0, 0, 0, 96, 97, 1, 0, 0, 0, 97, 98, 6, 16, 0, 0, 98, 34, 1, 0, 0, 0, 99, 101, 7, 4, 0, 0, 100, 99, 1, 0, 0, 0, 101, 102, 1, 0, 0, 0, 102, 100, 1, 0, 0, 0, 102, 103, 1, 0, 0, 0, 103, 36, 1, 0, 0, 0, 104, 109, 5, 34, 0, 0, 105, 108, 3, 43, 21, 0, 106, 108, 3, 45, 22, 0, 107, 105, 1, 0, 0, 0, 107, 106, 1, 0, 0, 0, 108, 111, 1, 0, 0, 0, 109, 107, 1, 0, 0, 0, 109, 110, 1, 0, 0, 0, 110, 112, 1, 0, 0, 0, 111, 109, 1, 0, 0, 0, 112, 113, 5, 34, 0, 0, 113, 38, 1, 0, 0, 0, 114, 115, 7, 5, 0, 0, 115, 40, 1, 0, 0, 0, 116, 117, 5, 117, 0, 0, 117, 118, 3, 39, 19, 0, 118, 119, 3, 39, 19, 0, 119, 120, 3, 39, 19, 0, 120, 121, 3, 39, 19, 0, 121, 42, 1, 0, 0, 0, 122, 125, 5, 92, 0, 0, 123, 126, 7, 6, 0, 0, 124, 126, 3, 41, 20, 0, 125, 123, 1, 0, 0, 0, 125, 124, 1, 0, 0, 0, 126, 44, 1, 0, 0, 0, 127, 128, 8, 7, 0, 0, 128, 46, 1, 0, 0, 0, 8, 0, 80, 88, 95, 102, 107, 109, 125, 1, 6, 0, 0] \ No newline at end of file diff --git a/lfr/antlrgen/reggie/reggieLexer.py b/lfr/antlrgen/reggie/reggieLexer.py index c6cf007..7f449fc 100644 --- a/lfr/antlrgen/reggie/reggieLexer.py +++ b/lfr/antlrgen/reggie/reggieLexer.py @@ -1,66 +1,1217 @@ -# Generated from /Users/krishna/CIDAR/reggie/reggie.g4 by ANTLR 4.9 -from antlr4 import * -from io import StringIO -from typing.io import TextIO +# Generated from ./reggie.g4 by ANTLR 4.10.1 import sys +from io import StringIO + +from antlr4 import * +if sys.version_info[1] > 5: + from typing import TextIO +else: + from typing.io import TextIO def serializedATN(): - with StringIO() as buf: - buf.write("\3\u608b\ua72a\u8133\ub9ed\u417c\u3be7\u7786\u5964\2\25") - buf.write("\u0083\b\1\4\2\t\2\4\3\t\3\4\4\t\4\4\5\t\5\4\6\t\6\4\7") - buf.write("\t\7\4\b\t\b\4\t\t\t\4\n\t\n\4\13\t\13\4\f\t\f\4\r\t\r") - buf.write("\4\16\t\16\4\17\t\17\4\20\t\20\4\21\t\21\4\22\t\22\4\23") - buf.write("\t\23\4\24\t\24\4\25\t\25\4\26\t\26\4\27\t\27\4\30\t\30") - buf.write("\3\2\3\2\3\3\3\3\3\4\3\4\3\5\3\5\3\6\3\6\3\7\3\7\3\b\3") - buf.write("\b\3\t\3\t\3\n\3\n\3\13\3\13\3\f\3\f\3\r\3\r\3\r\3\16") - buf.write("\3\16\3\16\3\16\3\17\3\17\7\17Q\n\17\f\17\16\17T\13\17") - buf.write("\3\20\3\20\3\21\6\21Y\n\21\r\21\16\21Z\3\21\3\21\3\22") - buf.write("\6\22`\n\22\r\22\16\22a\3\22\3\22\3\23\6\23g\n\23\r\23") - buf.write("\16\23h\3\24\3\24\3\24\7\24n\n\24\f\24\16\24q\13\24\3") - buf.write("\24\3\24\3\25\3\25\3\26\3\26\3\26\3\26\3\26\3\26\3\27") - buf.write("\3\27\3\27\5\27\u0080\n\27\3\30\3\30\2\2\31\3\3\5\4\7") - buf.write("\5\t\6\13\7\r\b\17\t\21\n\23\13\25\f\27\r\31\16\33\17") - buf.write("\35\20\37\21!\22#\23%\24\'\25)\2+\2-\2/\2\3\2\n\5\2C\\") - buf.write("aac|\6\2\62;C\\aac|\4\2\13\13\"\"\4\2\f\f\17\17\3\2\62") - buf.write(";\5\2\62;CHch\n\2$$\61\61^^ddhhppttvv\5\2\2!$$^^\2\u0085") - buf.write("\2\3\3\2\2\2\2\5\3\2\2\2\2\7\3\2\2\2\2\t\3\2\2\2\2\13") - buf.write("\3\2\2\2\2\r\3\2\2\2\2\17\3\2\2\2\2\21\3\2\2\2\2\23\3") - buf.write("\2\2\2\2\25\3\2\2\2\2\27\3\2\2\2\2\31\3\2\2\2\2\33\3\2") - buf.write("\2\2\2\35\3\2\2\2\2\37\3\2\2\2\2!\3\2\2\2\2#\3\2\2\2\2") - buf.write("%\3\2\2\2\2\'\3\2\2\2\3\61\3\2\2\2\5\63\3\2\2\2\7\65\3") - buf.write("\2\2\2\t\67\3\2\2\2\139\3\2\2\2\r;\3\2\2\2\17=\3\2\2\2") - buf.write("\21?\3\2\2\2\23A\3\2\2\2\25C\3\2\2\2\27E\3\2\2\2\31G\3") - buf.write("\2\2\2\33J\3\2\2\2\35N\3\2\2\2\37U\3\2\2\2!X\3\2\2\2#") - buf.write("_\3\2\2\2%f\3\2\2\2\'j\3\2\2\2)t\3\2\2\2+v\3\2\2\2-|\3") - buf.write("\2\2\2/\u0081\3\2\2\2\61\62\7}\2\2\62\4\3\2\2\2\63\64") - buf.write("\7.\2\2\64\6\3\2\2\2\65\66\7\177\2\2\66\b\3\2\2\2\678") - buf.write("\7*\2\28\n\3\2\2\29:\7+\2\2:\f\3\2\2\2;<\7<\2\2<\16\3") - buf.write("\2\2\2=>\7]\2\2>\20\3\2\2\2?@\7_\2\2@\22\3\2\2\2AB\7,") - buf.write("\2\2B\24\3\2\2\2CD\7-\2\2D\26\3\2\2\2EF\7~\2\2F\30\3\2") - buf.write("\2\2GH\7/\2\2HI\7@\2\2I\32\3\2\2\2JK\7>\2\2KL\7/\2\2L") - buf.write("M\7@\2\2M\34\3\2\2\2NR\t\2\2\2OQ\t\3\2\2PO\3\2\2\2QT\3") - buf.write("\2\2\2RP\3\2\2\2RS\3\2\2\2S\36\3\2\2\2TR\3\2\2\2UV\7A") - buf.write("\2\2V \3\2\2\2WY\t\4\2\2XW\3\2\2\2YZ\3\2\2\2ZX\3\2\2\2") - buf.write("Z[\3\2\2\2[\\\3\2\2\2\\]\b\21\2\2]\"\3\2\2\2^`\t\5\2\2") - buf.write("_^\3\2\2\2`a\3\2\2\2a_\3\2\2\2ab\3\2\2\2bc\3\2\2\2cd\b") - buf.write("\22\2\2d$\3\2\2\2eg\t\6\2\2fe\3\2\2\2gh\3\2\2\2hf\3\2") - buf.write("\2\2hi\3\2\2\2i&\3\2\2\2jo\7$\2\2kn\5-\27\2ln\5/\30\2") - buf.write("mk\3\2\2\2ml\3\2\2\2nq\3\2\2\2om\3\2\2\2op\3\2\2\2pr\3") - buf.write("\2\2\2qo\3\2\2\2rs\7$\2\2s(\3\2\2\2tu\t\7\2\2u*\3\2\2") - buf.write("\2vw\7w\2\2wx\5)\25\2xy\5)\25\2yz\5)\25\2z{\5)\25\2{,") - buf.write("\3\2\2\2|\177\7^\2\2}\u0080\t\b\2\2~\u0080\5+\26\2\177") - buf.write("}\3\2\2\2\177~\3\2\2\2\u0080.\3\2\2\2\u0081\u0082\n\t") - buf.write("\2\2\u0082\60\3\2\2\2\n\2RZahmo\177\3\b\2\2") - return buf.getvalue() + return [ + 4, + 0, + 19, + 129, + 6, + -1, + 2, + 0, + 7, + 0, + 2, + 1, + 7, + 1, + 2, + 2, + 7, + 2, + 2, + 3, + 7, + 3, + 2, + 4, + 7, + 4, + 2, + 5, + 7, + 5, + 2, + 6, + 7, + 6, + 2, + 7, + 7, + 7, + 2, + 8, + 7, + 8, + 2, + 9, + 7, + 9, + 2, + 10, + 7, + 10, + 2, + 11, + 7, + 11, + 2, + 12, + 7, + 12, + 2, + 13, + 7, + 13, + 2, + 14, + 7, + 14, + 2, + 15, + 7, + 15, + 2, + 16, + 7, + 16, + 2, + 17, + 7, + 17, + 2, + 18, + 7, + 18, + 2, + 19, + 7, + 19, + 2, + 20, + 7, + 20, + 2, + 21, + 7, + 21, + 2, + 22, + 7, + 22, + 1, + 0, + 1, + 0, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 2, + 1, + 3, + 1, + 3, + 1, + 4, + 1, + 4, + 1, + 5, + 1, + 5, + 1, + 6, + 1, + 6, + 1, + 7, + 1, + 7, + 1, + 8, + 1, + 8, + 1, + 9, + 1, + 9, + 1, + 10, + 1, + 10, + 1, + 11, + 1, + 11, + 1, + 11, + 1, + 12, + 1, + 12, + 1, + 12, + 1, + 12, + 1, + 13, + 1, + 13, + 5, + 13, + 79, + 8, + 13, + 10, + 13, + 12, + 13, + 82, + 9, + 13, + 1, + 14, + 1, + 14, + 1, + 15, + 4, + 15, + 87, + 8, + 15, + 11, + 15, + 12, + 15, + 88, + 1, + 15, + 1, + 15, + 1, + 16, + 4, + 16, + 94, + 8, + 16, + 11, + 16, + 12, + 16, + 95, + 1, + 16, + 1, + 16, + 1, + 17, + 4, + 17, + 101, + 8, + 17, + 11, + 17, + 12, + 17, + 102, + 1, + 18, + 1, + 18, + 1, + 18, + 5, + 18, + 108, + 8, + 18, + 10, + 18, + 12, + 18, + 111, + 9, + 18, + 1, + 18, + 1, + 18, + 1, + 19, + 1, + 19, + 1, + 20, + 1, + 20, + 1, + 20, + 1, + 20, + 1, + 20, + 1, + 20, + 1, + 21, + 1, + 21, + 1, + 21, + 3, + 21, + 126, + 8, + 21, + 1, + 22, + 1, + 22, + 0, + 0, + 23, + 1, + 1, + 3, + 2, + 5, + 3, + 7, + 4, + 9, + 5, + 11, + 6, + 13, + 7, + 15, + 8, + 17, + 9, + 19, + 10, + 21, + 11, + 23, + 12, + 25, + 13, + 27, + 14, + 29, + 15, + 31, + 16, + 33, + 17, + 35, + 18, + 37, + 19, + 39, + 0, + 41, + 0, + 43, + 0, + 45, + 0, + 1, + 0, + 8, + 3, + 0, + 65, + 90, + 95, + 95, + 97, + 122, + 4, + 0, + 48, + 57, + 65, + 90, + 95, + 95, + 97, + 122, + 2, + 0, + 9, + 9, + 32, + 32, + 2, + 0, + 10, + 10, + 13, + 13, + 1, + 0, + 48, + 57, + 3, + 0, + 48, + 57, + 65, + 70, + 97, + 102, + 8, + 0, + 34, + 34, + 47, + 47, + 92, + 92, + 98, + 98, + 102, + 102, + 110, + 110, + 114, + 114, + 116, + 116, + 3, + 0, + 0, + 31, + 34, + 34, + 92, + 92, + 131, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 3, + 1, + 0, + 0, + 0, + 0, + 5, + 1, + 0, + 0, + 0, + 0, + 7, + 1, + 0, + 0, + 0, + 0, + 9, + 1, + 0, + 0, + 0, + 0, + 11, + 1, + 0, + 0, + 0, + 0, + 13, + 1, + 0, + 0, + 0, + 0, + 15, + 1, + 0, + 0, + 0, + 0, + 17, + 1, + 0, + 0, + 0, + 0, + 19, + 1, + 0, + 0, + 0, + 0, + 21, + 1, + 0, + 0, + 0, + 0, + 23, + 1, + 0, + 0, + 0, + 0, + 25, + 1, + 0, + 0, + 0, + 0, + 27, + 1, + 0, + 0, + 0, + 0, + 29, + 1, + 0, + 0, + 0, + 0, + 31, + 1, + 0, + 0, + 0, + 0, + 33, + 1, + 0, + 0, + 0, + 0, + 35, + 1, + 0, + 0, + 0, + 0, + 37, + 1, + 0, + 0, + 0, + 1, + 47, + 1, + 0, + 0, + 0, + 3, + 49, + 1, + 0, + 0, + 0, + 5, + 51, + 1, + 0, + 0, + 0, + 7, + 53, + 1, + 0, + 0, + 0, + 9, + 55, + 1, + 0, + 0, + 0, + 11, + 57, + 1, + 0, + 0, + 0, + 13, + 59, + 1, + 0, + 0, + 0, + 15, + 61, + 1, + 0, + 0, + 0, + 17, + 63, + 1, + 0, + 0, + 0, + 19, + 65, + 1, + 0, + 0, + 0, + 21, + 67, + 1, + 0, + 0, + 0, + 23, + 69, + 1, + 0, + 0, + 0, + 25, + 72, + 1, + 0, + 0, + 0, + 27, + 76, + 1, + 0, + 0, + 0, + 29, + 83, + 1, + 0, + 0, + 0, + 31, + 86, + 1, + 0, + 0, + 0, + 33, + 93, + 1, + 0, + 0, + 0, + 35, + 100, + 1, + 0, + 0, + 0, + 37, + 104, + 1, + 0, + 0, + 0, + 39, + 114, + 1, + 0, + 0, + 0, + 41, + 116, + 1, + 0, + 0, + 0, + 43, + 122, + 1, + 0, + 0, + 0, + 45, + 127, + 1, + 0, + 0, + 0, + 47, + 48, + 5, + 123, + 0, + 0, + 48, + 2, + 1, + 0, + 0, + 0, + 49, + 50, + 5, + 44, + 0, + 0, + 50, + 4, + 1, + 0, + 0, + 0, + 51, + 52, + 5, + 125, + 0, + 0, + 52, + 6, + 1, + 0, + 0, + 0, + 53, + 54, + 5, + 40, + 0, + 0, + 54, + 8, + 1, + 0, + 0, + 0, + 55, + 56, + 5, + 41, + 0, + 0, + 56, + 10, + 1, + 0, + 0, + 0, + 57, + 58, + 5, + 58, + 0, + 0, + 58, + 12, + 1, + 0, + 0, + 0, + 59, + 60, + 5, + 91, + 0, + 0, + 60, + 14, + 1, + 0, + 0, + 0, + 61, + 62, + 5, + 93, + 0, + 0, + 62, + 16, + 1, + 0, + 0, + 0, + 63, + 64, + 5, + 42, + 0, + 0, + 64, + 18, + 1, + 0, + 0, + 0, + 65, + 66, + 5, + 43, + 0, + 0, + 66, + 20, + 1, + 0, + 0, + 0, + 67, + 68, + 5, + 124, + 0, + 0, + 68, + 22, + 1, + 0, + 0, + 0, + 69, + 70, + 5, + 45, + 0, + 0, + 70, + 71, + 5, + 62, + 0, + 0, + 71, + 24, + 1, + 0, + 0, + 0, + 72, + 73, + 5, + 60, + 0, + 0, + 73, + 74, + 5, + 45, + 0, + 0, + 74, + 75, + 5, + 62, + 0, + 0, + 75, + 26, + 1, + 0, + 0, + 0, + 76, + 80, + 7, + 0, + 0, + 0, + 77, + 79, + 7, + 1, + 0, + 0, + 78, + 77, + 1, + 0, + 0, + 0, + 79, + 82, + 1, + 0, + 0, + 0, + 80, + 78, + 1, + 0, + 0, + 0, + 80, + 81, + 1, + 0, + 0, + 0, + 81, + 28, + 1, + 0, + 0, + 0, + 82, + 80, + 1, + 0, + 0, + 0, + 83, + 84, + 5, + 63, + 0, + 0, + 84, + 30, + 1, + 0, + 0, + 0, + 85, + 87, + 7, + 2, + 0, + 0, + 86, + 85, + 1, + 0, + 0, + 0, + 87, + 88, + 1, + 0, + 0, + 0, + 88, + 86, + 1, + 0, + 0, + 0, + 88, + 89, + 1, + 0, + 0, + 0, + 89, + 90, + 1, + 0, + 0, + 0, + 90, + 91, + 6, + 15, + 0, + 0, + 91, + 32, + 1, + 0, + 0, + 0, + 92, + 94, + 7, + 3, + 0, + 0, + 93, + 92, + 1, + 0, + 0, + 0, + 94, + 95, + 1, + 0, + 0, + 0, + 95, + 93, + 1, + 0, + 0, + 0, + 95, + 96, + 1, + 0, + 0, + 0, + 96, + 97, + 1, + 0, + 0, + 0, + 97, + 98, + 6, + 16, + 0, + 0, + 98, + 34, + 1, + 0, + 0, + 0, + 99, + 101, + 7, + 4, + 0, + 0, + 100, + 99, + 1, + 0, + 0, + 0, + 101, + 102, + 1, + 0, + 0, + 0, + 102, + 100, + 1, + 0, + 0, + 0, + 102, + 103, + 1, + 0, + 0, + 0, + 103, + 36, + 1, + 0, + 0, + 0, + 104, + 109, + 5, + 34, + 0, + 0, + 105, + 108, + 3, + 43, + 21, + 0, + 106, + 108, + 3, + 45, + 22, + 0, + 107, + 105, + 1, + 0, + 0, + 0, + 107, + 106, + 1, + 0, + 0, + 0, + 108, + 111, + 1, + 0, + 0, + 0, + 109, + 107, + 1, + 0, + 0, + 0, + 109, + 110, + 1, + 0, + 0, + 0, + 110, + 112, + 1, + 0, + 0, + 0, + 111, + 109, + 1, + 0, + 0, + 0, + 112, + 113, + 5, + 34, + 0, + 0, + 113, + 38, + 1, + 0, + 0, + 0, + 114, + 115, + 7, + 5, + 0, + 0, + 115, + 40, + 1, + 0, + 0, + 0, + 116, + 117, + 5, + 117, + 0, + 0, + 117, + 118, + 3, + 39, + 19, + 0, + 118, + 119, + 3, + 39, + 19, + 0, + 119, + 120, + 3, + 39, + 19, + 0, + 120, + 121, + 3, + 39, + 19, + 0, + 121, + 42, + 1, + 0, + 0, + 0, + 122, + 125, + 5, + 92, + 0, + 0, + 123, + 126, + 7, + 6, + 0, + 0, + 124, + 126, + 3, + 41, + 20, + 0, + 125, + 123, + 1, + 0, + 0, + 0, + 125, + 124, + 1, + 0, + 0, + 0, + 126, + 44, + 1, + 0, + 0, + 0, + 127, + 128, + 8, + 7, + 0, + 0, + 128, + 46, + 1, + 0, + 0, + 0, + 8, + 0, + 80, + 88, + 95, + 102, + 107, + 109, + 125, + 1, + 6, + 0, + 0, + ] class reggieLexer(Lexer): - atn = ATNDeserializer().deserialize(serializedATN()) - decisionsToDFA = [ DFA(ds, i) for i, ds in enumerate(atn.decisionToState) ] + decisionsToDFA = [DFA(ds, i) for i, ds in enumerate(atn.decisionToState)] T__0 = 1 T__1 = 2 @@ -82,29 +1233,71 @@ class reggieLexer(Lexer): INT = 18 STRING = 19 - channelNames = [ u"DEFAULT_TOKEN_CHANNEL", u"HIDDEN" ] + channelNames = ["DEFAULT_TOKEN_CHANNEL", "HIDDEN"] - modeNames = [ "DEFAULT_MODE" ] + modeNames = ["DEFAULT_MODE"] - literalNames = [ "", - "'{'", "','", "'}'", "'('", "')'", "':'", "'['", "']'", "'*'", - "'+'", "'|'", "'->'", "'<->'", "'?'" ] + literalNames = [ + "", + "'{'", + "','", + "'}'", + "'('", + "')'", + "':'", + "'['", + "']'", + "'*'", + "'+'", + "'|'", + "'->'", + "'<->'", + "'?'", + ] - symbolicNames = [ "", - "ID", "QUESTIONMARK_WILDCARD", "WS", "NL", "INT", "STRING" ] + symbolicNames = [ + "", + "ID", + "QUESTIONMARK_WILDCARD", + "WS", + "NL", + "INT", + "STRING", + ] - ruleNames = [ "T__0", "T__1", "T__2", "T__3", "T__4", "T__5", "T__6", - "T__7", "T__8", "T__9", "T__10", "T__11", "T__12", "ID", - "QUESTIONMARK_WILDCARD", "WS", "NL", "INT", "STRING", - "HEX", "UNICODE", "ESC", "SAFECODEPOINT" ] + ruleNames = [ + "T__0", + "T__1", + "T__2", + "T__3", + "T__4", + "T__5", + "T__6", + "T__7", + "T__8", + "T__9", + "T__10", + "T__11", + "T__12", + "ID", + "QUESTIONMARK_WILDCARD", + "WS", + "NL", + "INT", + "STRING", + "HEX", + "UNICODE", + "ESC", + "SAFECODEPOINT", + ] grammarFileName = "reggie.g4" - def __init__(self, input=None, output:TextIO = sys.stdout): + def __init__(self, input=None, output: TextIO = sys.stdout): super().__init__(input, output) - self.checkVersion("4.9") - self._interp = LexerATNSimulator(self, self.atn, self.decisionsToDFA, PredictionContextCache()) + self.checkVersion("4.10.1") + self._interp = LexerATNSimulator( + self, self.atn, self.decisionsToDFA, PredictionContextCache() + ) self._actions = None self._predicates = None - - diff --git a/lfr/antlrgen/reggie/reggieListener.py b/lfr/antlrgen/reggie/reggieListener.py index 8728cc6..e8c4d0c 100644 --- a/lfr/antlrgen/reggie/reggieListener.py +++ b/lfr/antlrgen/reggie/reggieListener.py @@ -1,14 +1,14 @@ -# Generated from /Users/krishna/CIDAR/reggie/reggie.g4 by ANTLR 4.9 +# Generated from ./reggie.g4 by ANTLR 4.10.1 from antlr4 import * if __name__ is not None and "." in __name__: from .reggieParser import reggieParser else: - from lfr.graphmatchParser import reggieParser + from reggieParser import reggieParser + # This class defines a complete listener for a parse tree produced by reggieParser. class reggieListener(ParseTreeListener): - # Enter a parse tree produced by reggieParser#graph. def enterGraph(self, ctx: reggieParser.GraphContext): pass @@ -142,4 +142,4 @@ def exitEdge(self, ctx: reggieParser.EdgeContext): pass -del reggieParser \ No newline at end of file +del reggieParser diff --git a/lfr/antlrgen/reggie/reggieParser.py b/lfr/antlrgen/reggie/reggieParser.py index dcf589e..1e6edf0 100644 --- a/lfr/antlrgen/reggie/reggieParser.py +++ b/lfr/antlrgen/reggie/reggieParser.py @@ -1,90 +1,1341 @@ -# Generated from /Users/krishna/CIDAR/reggie/reggie.g4 by ANTLR 4.9 +# Generated from ./reggie.g4 by ANTLR 4.10.1 # encoding: utf-8 -from antlr4 import * -from io import StringIO import sys +from io import StringIO + +from antlr4 import * + if sys.version_info[1] > 5: - from typing import TextIO + from typing import TextIO else: - from typing.io import TextIO + from typing.io import TextIO def serializedATN(): - with StringIO() as buf: - buf.write("\3\u608b\ua72a\u8133\ub9ed\u417c\u3be7\u7786\u5964\3\25") - buf.write("\u0098\4\2\t\2\4\3\t\3\4\4\t\4\4\5\t\5\4\6\t\6\4\7\t\7") - buf.write("\4\b\t\b\4\t\t\t\4\n\t\n\4\13\t\13\4\f\t\f\4\r\t\r\4\16") - buf.write("\t\16\4\17\t\17\4\20\t\20\4\21\t\21\3\2\3\2\3\2\3\2\7") - buf.write("\2\'\n\2\f\2\16\2*\13\2\3\2\3\2\3\3\3\3\3\3\3\3\3\3\3") - buf.write("\3\5\3\64\n\3\3\4\3\4\3\4\5\49\n\4\3\5\3\5\3\5\5\5>\n") - buf.write("\5\3\6\3\6\3\6\3\6\3\6\3\6\3\6\7\6G\n\6\f\6\16\6J\13\6") - buf.write("\3\6\3\6\3\7\3\7\3\7\5\7Q\n\7\3\7\5\7T\n\7\3\7\7\7W\n") - buf.write("\7\f\7\16\7Z\13\7\3\b\3\b\3\b\3\b\7\b`\n\b\f\b\16\bc\13") - buf.write("\b\3\b\3\b\3\t\3\t\3\t\5\tj\n\t\3\n\3\n\3\n\3\n\3\13\3") - buf.write("\13\3\f\3\f\3\r\3\r\3\16\3\16\3\16\7\16y\n\16\f\16\16") - buf.write("\16|\13\16\3\16\3\16\3\16\3\16\7\16\u0082\n\16\f\16\16") - buf.write("\16\u0085\13\16\3\16\3\16\5\16\u0089\n\16\3\17\3\17\3") - buf.write("\20\3\20\3\20\3\20\7\20\u0091\n\20\f\20\16\20\u0094\13") - buf.write("\20\3\21\3\21\3\21\2\2\22\2\4\6\b\n\f\16\20\22\24\26\30") - buf.write("\32\34\36 \2\4\3\2\20\21\3\2\16\17\2\u0098\2\"\3\2\2\2") - buf.write("\4\63\3\2\2\2\68\3\2\2\2\b=\3\2\2\2\n?\3\2\2\2\fM\3\2") - buf.write("\2\2\16[\3\2\2\2\20i\3\2\2\2\22k\3\2\2\2\24o\3\2\2\2\26") - buf.write("q\3\2\2\2\30s\3\2\2\2\32\u0088\3\2\2\2\34\u008a\3\2\2") - buf.write("\2\36\u008c\3\2\2\2 \u0095\3\2\2\2\"#\7\3\2\2#(\5\4\3") - buf.write("\2$%\7\4\2\2%\'\5\4\3\2&$\3\2\2\2\'*\3\2\2\2(&\3\2\2\2") - buf.write("()\3\2\2\2)+\3\2\2\2*(\3\2\2\2+,\7\5\2\2,\3\3\2\2\2-\64") - buf.write("\5\b\5\2./\7\6\2\2/\60\5\b\5\2\60\61\7\7\2\2\61\62\5\6") - buf.write("\4\2\62\64\3\2\2\2\63-\3\2\2\2\63.\3\2\2\2\64\5\3\2\2") - buf.write("\2\659\5\22\n\2\669\5\26\f\2\679\5\24\13\28\65\3\2\2\2") - buf.write("8\66\3\2\2\28\67\3\2\2\29\7\3\2\2\2:>\5\n\6\2;>\5\36\20") - buf.write("\2<>\5\f\7\2=:\3\2\2\2=;\3\2\2\2=<\3\2\2\2>\t\3\2\2\2") - buf.write("?@\7\20\2\2@A\7\b\2\2AB\3\2\2\2BC\7\3\2\2CH\5\36\20\2") - buf.write("DE\7\4\2\2EG\5\36\20\2FD\3\2\2\2GJ\3\2\2\2HF\3\2\2\2H") - buf.write("I\3\2\2\2IK\3\2\2\2JH\3\2\2\2KL\7\5\2\2L\13\3\2\2\2MP") - buf.write("\5\30\r\2NO\7\b\2\2OQ\5\32\16\2PN\3\2\2\2PQ\3\2\2\2QS") - buf.write("\3\2\2\2RT\5\16\b\2SR\3\2\2\2ST\3\2\2\2TX\3\2\2\2UW\5") - buf.write("\20\t\2VU\3\2\2\2WZ\3\2\2\2XV\3\2\2\2XY\3\2\2\2Y\r\3\2") - buf.write("\2\2ZX\3\2\2\2[\\\7\3\2\2\\a\7\25\2\2]^\7\4\2\2^`\7\25") - buf.write("\2\2_]\3\2\2\2`c\3\2\2\2a_\3\2\2\2ab\3\2\2\2bd\3\2\2\2") - buf.write("ca\3\2\2\2de\7\5\2\2e\17\3\2\2\2fj\5\22\n\2gj\5\24\13") - buf.write("\2hj\5\26\f\2if\3\2\2\2ig\3\2\2\2ih\3\2\2\2j\21\3\2\2") - buf.write("\2kl\7\t\2\2lm\7\24\2\2mn\7\n\2\2n\23\3\2\2\2op\7\13\2") - buf.write("\2p\25\3\2\2\2qr\7\f\2\2r\27\3\2\2\2st\t\2\2\2t\31\3\2") - buf.write("\2\2uz\5\34\17\2vw\7\r\2\2wy\5\34\17\2xv\3\2\2\2y|\3\2") - buf.write("\2\2zx\3\2\2\2z{\3\2\2\2{\u0089\3\2\2\2|z\3\2\2\2}~\7") - buf.write("\6\2\2~\u0083\5\34\17\2\177\u0080\7\r\2\2\u0080\u0082") - buf.write("\5\34\17\2\u0081\177\3\2\2\2\u0082\u0085\3\2\2\2\u0083") - buf.write("\u0081\3\2\2\2\u0083\u0084\3\2\2\2\u0084\u0086\3\2\2\2") - buf.write("\u0085\u0083\3\2\2\2\u0086\u0087\7\7\2\2\u0087\u0089\3") - buf.write("\2\2\2\u0088u\3\2\2\2\u0088}\3\2\2\2\u0089\33\3\2\2\2") - buf.write("\u008a\u008b\7\20\2\2\u008b\35\3\2\2\2\u008c\u0092\5\f") - buf.write("\7\2\u008d\u008e\5 \21\2\u008e\u008f\5\f\7\2\u008f\u0091") - buf.write("\3\2\2\2\u0090\u008d\3\2\2\2\u0091\u0094\3\2\2\2\u0092") - buf.write("\u0090\3\2\2\2\u0092\u0093\3\2\2\2\u0093\37\3\2\2\2\u0094") - buf.write("\u0092\3\2\2\2\u0095\u0096\t\3\2\2\u0096!\3\2\2\2\20(") - buf.write("\638=HPSXaiz\u0083\u0088\u0092") - return buf.getvalue() - - -class reggieParser ( Parser ): - + return [ + 4, + 1, + 19, + 150, + 2, + 0, + 7, + 0, + 2, + 1, + 7, + 1, + 2, + 2, + 7, + 2, + 2, + 3, + 7, + 3, + 2, + 4, + 7, + 4, + 2, + 5, + 7, + 5, + 2, + 6, + 7, + 6, + 2, + 7, + 7, + 7, + 2, + 8, + 7, + 8, + 2, + 9, + 7, + 9, + 2, + 10, + 7, + 10, + 2, + 11, + 7, + 11, + 2, + 12, + 7, + 12, + 2, + 13, + 7, + 13, + 2, + 14, + 7, + 14, + 2, + 15, + 7, + 15, + 1, + 0, + 1, + 0, + 1, + 0, + 1, + 0, + 5, + 0, + 37, + 8, + 0, + 10, + 0, + 12, + 0, + 40, + 9, + 0, + 1, + 0, + 1, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 3, + 1, + 50, + 8, + 1, + 1, + 2, + 1, + 2, + 1, + 2, + 3, + 2, + 55, + 8, + 2, + 1, + 3, + 1, + 3, + 1, + 3, + 3, + 3, + 60, + 8, + 3, + 1, + 4, + 1, + 4, + 1, + 4, + 1, + 4, + 1, + 4, + 1, + 4, + 1, + 4, + 5, + 4, + 69, + 8, + 4, + 10, + 4, + 12, + 4, + 72, + 9, + 4, + 1, + 4, + 1, + 4, + 1, + 5, + 1, + 5, + 1, + 5, + 3, + 5, + 79, + 8, + 5, + 1, + 5, + 3, + 5, + 82, + 8, + 5, + 1, + 5, + 5, + 5, + 85, + 8, + 5, + 10, + 5, + 12, + 5, + 88, + 9, + 5, + 1, + 6, + 1, + 6, + 1, + 6, + 1, + 6, + 5, + 6, + 94, + 8, + 6, + 10, + 6, + 12, + 6, + 97, + 9, + 6, + 1, + 6, + 1, + 6, + 1, + 7, + 1, + 7, + 1, + 7, + 3, + 7, + 104, + 8, + 7, + 1, + 8, + 1, + 8, + 1, + 8, + 1, + 8, + 1, + 9, + 1, + 9, + 1, + 10, + 1, + 10, + 1, + 11, + 1, + 11, + 1, + 12, + 1, + 12, + 1, + 12, + 5, + 12, + 119, + 8, + 12, + 10, + 12, + 12, + 12, + 122, + 9, + 12, + 1, + 12, + 1, + 12, + 1, + 12, + 1, + 12, + 5, + 12, + 128, + 8, + 12, + 10, + 12, + 12, + 12, + 131, + 9, + 12, + 1, + 12, + 1, + 12, + 3, + 12, + 135, + 8, + 12, + 1, + 13, + 1, + 13, + 1, + 14, + 1, + 14, + 1, + 14, + 1, + 14, + 5, + 14, + 143, + 8, + 14, + 10, + 14, + 12, + 14, + 146, + 9, + 14, + 1, + 15, + 1, + 15, + 1, + 15, + 0, + 0, + 16, + 0, + 2, + 4, + 6, + 8, + 10, + 12, + 14, + 16, + 18, + 20, + 22, + 24, + 26, + 28, + 30, + 0, + 2, + 1, + 0, + 14, + 15, + 1, + 0, + 12, + 13, + 150, + 0, + 32, + 1, + 0, + 0, + 0, + 2, + 49, + 1, + 0, + 0, + 0, + 4, + 54, + 1, + 0, + 0, + 0, + 6, + 59, + 1, + 0, + 0, + 0, + 8, + 61, + 1, + 0, + 0, + 0, + 10, + 75, + 1, + 0, + 0, + 0, + 12, + 89, + 1, + 0, + 0, + 0, + 14, + 103, + 1, + 0, + 0, + 0, + 16, + 105, + 1, + 0, + 0, + 0, + 18, + 109, + 1, + 0, + 0, + 0, + 20, + 111, + 1, + 0, + 0, + 0, + 22, + 113, + 1, + 0, + 0, + 0, + 24, + 134, + 1, + 0, + 0, + 0, + 26, + 136, + 1, + 0, + 0, + 0, + 28, + 138, + 1, + 0, + 0, + 0, + 30, + 147, + 1, + 0, + 0, + 0, + 32, + 33, + 5, + 1, + 0, + 0, + 33, + 38, + 3, + 2, + 1, + 0, + 34, + 35, + 5, + 2, + 0, + 0, + 35, + 37, + 3, + 2, + 1, + 0, + 36, + 34, + 1, + 0, + 0, + 0, + 37, + 40, + 1, + 0, + 0, + 0, + 38, + 36, + 1, + 0, + 0, + 0, + 38, + 39, + 1, + 0, + 0, + 0, + 39, + 41, + 1, + 0, + 0, + 0, + 40, + 38, + 1, + 0, + 0, + 0, + 41, + 42, + 5, + 3, + 0, + 0, + 42, + 1, + 1, + 0, + 0, + 0, + 43, + 50, + 3, + 6, + 3, + 0, + 44, + 45, + 5, + 4, + 0, + 0, + 45, + 46, + 3, + 6, + 3, + 0, + 46, + 47, + 5, + 5, + 0, + 0, + 47, + 48, + 3, + 4, + 2, + 0, + 48, + 50, + 1, + 0, + 0, + 0, + 49, + 43, + 1, + 0, + 0, + 0, + 49, + 44, + 1, + 0, + 0, + 0, + 50, + 3, + 1, + 0, + 0, + 0, + 51, + 55, + 3, + 16, + 8, + 0, + 52, + 55, + 3, + 20, + 10, + 0, + 53, + 55, + 3, + 18, + 9, + 0, + 54, + 51, + 1, + 0, + 0, + 0, + 54, + 52, + 1, + 0, + 0, + 0, + 54, + 53, + 1, + 0, + 0, + 0, + 55, + 5, + 1, + 0, + 0, + 0, + 56, + 60, + 3, + 8, + 4, + 0, + 57, + 60, + 3, + 28, + 14, + 0, + 58, + 60, + 3, + 10, + 5, + 0, + 59, + 56, + 1, + 0, + 0, + 0, + 59, + 57, + 1, + 0, + 0, + 0, + 59, + 58, + 1, + 0, + 0, + 0, + 60, + 7, + 1, + 0, + 0, + 0, + 61, + 62, + 5, + 14, + 0, + 0, + 62, + 63, + 5, + 6, + 0, + 0, + 63, + 64, + 1, + 0, + 0, + 0, + 64, + 65, + 5, + 1, + 0, + 0, + 65, + 70, + 3, + 28, + 14, + 0, + 66, + 67, + 5, + 2, + 0, + 0, + 67, + 69, + 3, + 28, + 14, + 0, + 68, + 66, + 1, + 0, + 0, + 0, + 69, + 72, + 1, + 0, + 0, + 0, + 70, + 68, + 1, + 0, + 0, + 0, + 70, + 71, + 1, + 0, + 0, + 0, + 71, + 73, + 1, + 0, + 0, + 0, + 72, + 70, + 1, + 0, + 0, + 0, + 73, + 74, + 5, + 3, + 0, + 0, + 74, + 9, + 1, + 0, + 0, + 0, + 75, + 78, + 3, + 22, + 11, + 0, + 76, + 77, + 5, + 6, + 0, + 0, + 77, + 79, + 3, + 24, + 12, + 0, + 78, + 76, + 1, + 0, + 0, + 0, + 78, + 79, + 1, + 0, + 0, + 0, + 79, + 81, + 1, + 0, + 0, + 0, + 80, + 82, + 3, + 12, + 6, + 0, + 81, + 80, + 1, + 0, + 0, + 0, + 81, + 82, + 1, + 0, + 0, + 0, + 82, + 86, + 1, + 0, + 0, + 0, + 83, + 85, + 3, + 14, + 7, + 0, + 84, + 83, + 1, + 0, + 0, + 0, + 85, + 88, + 1, + 0, + 0, + 0, + 86, + 84, + 1, + 0, + 0, + 0, + 86, + 87, + 1, + 0, + 0, + 0, + 87, + 11, + 1, + 0, + 0, + 0, + 88, + 86, + 1, + 0, + 0, + 0, + 89, + 90, + 5, + 1, + 0, + 0, + 90, + 95, + 5, + 19, + 0, + 0, + 91, + 92, + 5, + 2, + 0, + 0, + 92, + 94, + 5, + 19, + 0, + 0, + 93, + 91, + 1, + 0, + 0, + 0, + 94, + 97, + 1, + 0, + 0, + 0, + 95, + 93, + 1, + 0, + 0, + 0, + 95, + 96, + 1, + 0, + 0, + 0, + 96, + 98, + 1, + 0, + 0, + 0, + 97, + 95, + 1, + 0, + 0, + 0, + 98, + 99, + 5, + 3, + 0, + 0, + 99, + 13, + 1, + 0, + 0, + 0, + 100, + 104, + 3, + 16, + 8, + 0, + 101, + 104, + 3, + 18, + 9, + 0, + 102, + 104, + 3, + 20, + 10, + 0, + 103, + 100, + 1, + 0, + 0, + 0, + 103, + 101, + 1, + 0, + 0, + 0, + 103, + 102, + 1, + 0, + 0, + 0, + 104, + 15, + 1, + 0, + 0, + 0, + 105, + 106, + 5, + 7, + 0, + 0, + 106, + 107, + 5, + 18, + 0, + 0, + 107, + 108, + 5, + 8, + 0, + 0, + 108, + 17, + 1, + 0, + 0, + 0, + 109, + 110, + 5, + 9, + 0, + 0, + 110, + 19, + 1, + 0, + 0, + 0, + 111, + 112, + 5, + 10, + 0, + 0, + 112, + 21, + 1, + 0, + 0, + 0, + 113, + 114, + 7, + 0, + 0, + 0, + 114, + 23, + 1, + 0, + 0, + 0, + 115, + 120, + 3, + 26, + 13, + 0, + 116, + 117, + 5, + 11, + 0, + 0, + 117, + 119, + 3, + 26, + 13, + 0, + 118, + 116, + 1, + 0, + 0, + 0, + 119, + 122, + 1, + 0, + 0, + 0, + 120, + 118, + 1, + 0, + 0, + 0, + 120, + 121, + 1, + 0, + 0, + 0, + 121, + 135, + 1, + 0, + 0, + 0, + 122, + 120, + 1, + 0, + 0, + 0, + 123, + 124, + 5, + 4, + 0, + 0, + 124, + 129, + 3, + 26, + 13, + 0, + 125, + 126, + 5, + 11, + 0, + 0, + 126, + 128, + 3, + 26, + 13, + 0, + 127, + 125, + 1, + 0, + 0, + 0, + 128, + 131, + 1, + 0, + 0, + 0, + 129, + 127, + 1, + 0, + 0, + 0, + 129, + 130, + 1, + 0, + 0, + 0, + 130, + 132, + 1, + 0, + 0, + 0, + 131, + 129, + 1, + 0, + 0, + 0, + 132, + 133, + 5, + 5, + 0, + 0, + 133, + 135, + 1, + 0, + 0, + 0, + 134, + 115, + 1, + 0, + 0, + 0, + 134, + 123, + 1, + 0, + 0, + 0, + 135, + 25, + 1, + 0, + 0, + 0, + 136, + 137, + 5, + 14, + 0, + 0, + 137, + 27, + 1, + 0, + 0, + 0, + 138, + 144, + 3, + 10, + 5, + 0, + 139, + 140, + 3, + 30, + 15, + 0, + 140, + 141, + 3, + 10, + 5, + 0, + 141, + 143, + 1, + 0, + 0, + 0, + 142, + 139, + 1, + 0, + 0, + 0, + 143, + 146, + 1, + 0, + 0, + 0, + 144, + 142, + 1, + 0, + 0, + 0, + 144, + 145, + 1, + 0, + 0, + 0, + 145, + 29, + 1, + 0, + 0, + 0, + 146, + 144, + 1, + 0, + 0, + 0, + 147, + 148, + 7, + 1, + 0, + 0, + 148, + 31, + 1, + 0, + 0, + 0, + 14, + 38, + 49, + 54, + 59, + 70, + 78, + 81, + 86, + 95, + 103, + 120, + 129, + 134, + 144, + ] + + +class reggieParser(Parser): grammarFileName = "reggie.g4" atn = ATNDeserializer().deserialize(serializedATN()) - decisionsToDFA = [ DFA(ds, i) for i, ds in enumerate(atn.decisionToState) ] + decisionsToDFA = [DFA(ds, i) for i, ds in enumerate(atn.decisionToState)] sharedContextCache = PredictionContextCache() - literalNames = [ "", "'{'", "','", "'}'", "'('", "')'", "':'", - "'['", "']'", "'*'", "'+'", "'|'", "'->'", "'<->'", - "", "'?'" ] - - symbolicNames = [ "", "", "", "", - "", "", "", "", - "", "", "", "", - "", "", "ID", "QUESTIONMARK_WILDCARD", - "WS", "NL", "INT", "STRING" ] + literalNames = [ + "", + "'{'", + "','", + "'}'", + "'('", + "')'", + "':'", + "'['", + "']'", + "'*'", + "'+'", + "'|'", + "'->'", + "'<->'", + "", + "'?'", + ] + + symbolicNames = [ + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "ID", + "QUESTIONMARK_WILDCARD", + "WS", + "NL", + "INT", + "STRING", + ] RULE_graph = 0 RULE_graphstatement = 1 @@ -103,79 +1354,90 @@ class reggieParser ( Parser ): RULE_vertex2vertex = 14 RULE_edge = 15 - ruleNames = [ "graph", "graphstatement", "statementmodifier", "basestatement", - "subgraph", "vertex", "coloringfilter", "structuralvertexpattern", - "intmodifier", "starmodifier", "plusmodifier", "structuralid", - "labelfilter", "label", "vertex2vertex", "edge" ] + ruleNames = [ + "graph", + "graphstatement", + "statementmodifier", + "basestatement", + "subgraph", + "vertex", + "coloringfilter", + "structuralvertexpattern", + "intmodifier", + "starmodifier", + "plusmodifier", + "structuralid", + "labelfilter", + "label", + "vertex2vertex", + "edge", + ] EOF = Token.EOF - T__0=1 - T__1=2 - T__2=3 - T__3=4 - T__4=5 - T__5=6 - T__6=7 - T__7=8 - T__8=9 - T__9=10 - T__10=11 - T__11=12 - T__12=13 - ID=14 - QUESTIONMARK_WILDCARD=15 - WS=16 - NL=17 - INT=18 - STRING=19 - - def __init__(self, input:TokenStream, output:TextIO = sys.stdout): + T__0 = 1 + T__1 = 2 + T__2 = 3 + T__3 = 4 + T__4 = 5 + T__5 = 6 + T__6 = 7 + T__7 = 8 + T__8 = 9 + T__9 = 10 + T__10 = 11 + T__11 = 12 + T__12 = 13 + ID = 14 + QUESTIONMARK_WILDCARD = 15 + WS = 16 + NL = 17 + INT = 18 + STRING = 19 + + def __init__(self, input: TokenStream, output: TextIO = sys.stdout): super().__init__(input, output) - self.checkVersion("4.9") - self._interp = ParserATNSimulator(self, self.atn, self.decisionsToDFA, self.sharedContextCache) + self.checkVersion("4.10.1") + self._interp = ParserATNSimulator( + self, self.atn, self.decisionsToDFA, self.sharedContextCache + ) self._predicates = None - - - class GraphContext(ParserRuleContext): + __slots__ = "parser" - def __init__(self, parser, parent:ParserRuleContext=None, invokingState:int=-1): + def __init__( + self, parser, parent: ParserRuleContext = None, invokingState: int = -1 + ): super().__init__(parent, invokingState) self.parser = parser - def graphstatement(self, i:int=None): + def graphstatement(self, i: int = None): if i is None: return self.getTypedRuleContexts(reggieParser.GraphstatementContext) else: - return self.getTypedRuleContext(reggieParser.GraphstatementContext,i) - + return self.getTypedRuleContext(reggieParser.GraphstatementContext, i) def getRuleIndex(self): return reggieParser.RULE_graph - def enterRule(self, listener:ParseTreeListener): - if hasattr( listener, "enterGraph" ): + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterGraph"): listener.enterGraph(self) - def exitRule(self, listener:ParseTreeListener): - if hasattr( listener, "exitGraph" ): + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitGraph"): listener.exitGraph(self) - def accept(self, visitor:ParseTreeVisitor): - if hasattr( visitor, "visitGraph" ): + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitGraph"): return visitor.visitGraph(self) else: return visitor.visitChildren(self) - - - def graph(self): - localctx = reggieParser.GraphContext(self, self._ctx, self.state) self.enterRule(localctx, 0, self.RULE_graph) - self._la = 0 # Token type + self._la = 0 # Token type try: self.enterOuterAlt(localctx, 1) self.state = 32 @@ -185,7 +1447,7 @@ def graph(self): self.state = 38 self._errHandler.sync(self) _la = self._input.LA(1) - while _la==reggieParser.T__1: + while _la == reggieParser.T__1: self.state = 34 self.match(reggieParser.T__1) self.state = 35 @@ -204,43 +1466,39 @@ def graph(self): self.exitRule() return localctx - class GraphstatementContext(ParserRuleContext): + __slots__ = "parser" - def __init__(self, parser, parent:ParserRuleContext=None, invokingState:int=-1): + def __init__( + self, parser, parent: ParserRuleContext = None, invokingState: int = -1 + ): super().__init__(parent, invokingState) self.parser = parser def basestatement(self): - return self.getTypedRuleContext(reggieParser.BasestatementContext,0) - + return self.getTypedRuleContext(reggieParser.BasestatementContext, 0) def statementmodifier(self): - return self.getTypedRuleContext(reggieParser.StatementmodifierContext,0) - + return self.getTypedRuleContext(reggieParser.StatementmodifierContext, 0) def getRuleIndex(self): return reggieParser.RULE_graphstatement - def enterRule(self, listener:ParseTreeListener): - if hasattr( listener, "enterGraphstatement" ): + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterGraphstatement"): listener.enterGraphstatement(self) - def exitRule(self, listener:ParseTreeListener): - if hasattr( listener, "exitGraphstatement" ): + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitGraphstatement"): listener.exitGraphstatement(self) - def accept(self, visitor:ParseTreeVisitor): - if hasattr( visitor, "visitGraphstatement" ): + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitGraphstatement"): return visitor.visitGraphstatement(self) else: return visitor.visitChildren(self) - - - def graphstatement(self): - localctx = reggieParser.GraphstatementContext(self, self._ctx, self.state) self.enterRule(localctx, 2, self.RULE_graphstatement) try: @@ -274,47 +1532,42 @@ def graphstatement(self): self.exitRule() return localctx - class StatementmodifierContext(ParserRuleContext): + __slots__ = "parser" - def __init__(self, parser, parent:ParserRuleContext=None, invokingState:int=-1): + def __init__( + self, parser, parent: ParserRuleContext = None, invokingState: int = -1 + ): super().__init__(parent, invokingState) self.parser = parser def intmodifier(self): - return self.getTypedRuleContext(reggieParser.IntmodifierContext,0) - + return self.getTypedRuleContext(reggieParser.IntmodifierContext, 0) def plusmodifier(self): - return self.getTypedRuleContext(reggieParser.PlusmodifierContext,0) - + return self.getTypedRuleContext(reggieParser.PlusmodifierContext, 0) def starmodifier(self): - return self.getTypedRuleContext(reggieParser.StarmodifierContext,0) - + return self.getTypedRuleContext(reggieParser.StarmodifierContext, 0) def getRuleIndex(self): return reggieParser.RULE_statementmodifier - def enterRule(self, listener:ParseTreeListener): - if hasattr( listener, "enterStatementmodifier" ): + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterStatementmodifier"): listener.enterStatementmodifier(self) - def exitRule(self, listener:ParseTreeListener): - if hasattr( listener, "exitStatementmodifier" ): + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitStatementmodifier"): listener.exitStatementmodifier(self) - def accept(self, visitor:ParseTreeVisitor): - if hasattr( visitor, "visitStatementmodifier" ): + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitStatementmodifier"): return visitor.visitStatementmodifier(self) else: return visitor.visitChildren(self) - - - def statementmodifier(self): - localctx = reggieParser.StatementmodifierContext(self, self._ctx, self.state) self.enterRule(localctx, 4, self.RULE_statementmodifier) try: @@ -347,53 +1600,48 @@ def statementmodifier(self): self.exitRule() return localctx - class BasestatementContext(ParserRuleContext): + __slots__ = "parser" - def __init__(self, parser, parent:ParserRuleContext=None, invokingState:int=-1): + def __init__( + self, parser, parent: ParserRuleContext = None, invokingState: int = -1 + ): super().__init__(parent, invokingState) self.parser = parser def subgraph(self): - return self.getTypedRuleContext(reggieParser.SubgraphContext,0) - + return self.getTypedRuleContext(reggieParser.SubgraphContext, 0) def vertex2vertex(self): - return self.getTypedRuleContext(reggieParser.Vertex2vertexContext,0) - + return self.getTypedRuleContext(reggieParser.Vertex2vertexContext, 0) def vertex(self): - return self.getTypedRuleContext(reggieParser.VertexContext,0) - + return self.getTypedRuleContext(reggieParser.VertexContext, 0) def getRuleIndex(self): return reggieParser.RULE_basestatement - def enterRule(self, listener:ParseTreeListener): - if hasattr( listener, "enterBasestatement" ): + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterBasestatement"): listener.enterBasestatement(self) - def exitRule(self, listener:ParseTreeListener): - if hasattr( listener, "exitBasestatement" ): + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitBasestatement"): listener.exitBasestatement(self) - def accept(self, visitor:ParseTreeVisitor): - if hasattr( visitor, "visitBasestatement" ): + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitBasestatement"): return visitor.visitBasestatement(self) else: return visitor.visitChildren(self) - - - def basestatement(self): - localctx = reggieParser.BasestatementContext(self, self._ctx, self.state) self.enterRule(localctx, 6, self.RULE_basestatement) try: self.state = 59 self._errHandler.sync(self) - la_ = self._interp.adaptivePredict(self._input,3,self._ctx) + la_ = self._interp.adaptivePredict(self._input, 3, self._ctx) if la_ == 1: self.enterOuterAlt(localctx, 1) self.state = 56 @@ -412,7 +1660,6 @@ def basestatement(self): self.vertex() pass - except RecognitionException as re: localctx.exception = re self._errHandler.reportError(self, re) @@ -421,19 +1668,20 @@ def basestatement(self): self.exitRule() return localctx - class SubgraphContext(ParserRuleContext): + __slots__ = "parser" - def __init__(self, parser, parent:ParserRuleContext=None, invokingState:int=-1): + def __init__( + self, parser, parent: ParserRuleContext = None, invokingState: int = -1 + ): super().__init__(parent, invokingState) self.parser = parser - def vertex2vertex(self, i:int=None): + def vertex2vertex(self, i: int = None): if i is None: return self.getTypedRuleContexts(reggieParser.Vertex2vertexContext) else: - return self.getTypedRuleContext(reggieParser.Vertex2vertexContext,i) - + return self.getTypedRuleContext(reggieParser.Vertex2vertexContext, i) def ID(self): return self.getToken(reggieParser.ID, 0) @@ -441,28 +1689,24 @@ def ID(self): def getRuleIndex(self): return reggieParser.RULE_subgraph - def enterRule(self, listener:ParseTreeListener): - if hasattr( listener, "enterSubgraph" ): + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterSubgraph"): listener.enterSubgraph(self) - def exitRule(self, listener:ParseTreeListener): - if hasattr( listener, "exitSubgraph" ): + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitSubgraph"): listener.exitSubgraph(self) - def accept(self, visitor:ParseTreeVisitor): - if hasattr( visitor, "visitSubgraph" ): + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitSubgraph"): return visitor.visitSubgraph(self) else: return visitor.visitChildren(self) - - - def subgraph(self): - localctx = reggieParser.SubgraphContext(self, self._ctx, self.state) self.enterRule(localctx, 8, self.RULE_subgraph) - self._la = 0 # Token type + self._la = 0 # Token type try: self.enterOuterAlt(localctx, 1) self.state = 61 @@ -476,7 +1720,7 @@ def subgraph(self): self.state = 70 self._errHandler.sync(self) _la = self._input.LA(1) - while _la==reggieParser.T__1: + while _la == reggieParser.T__1: self.state = 66 self.match(reggieParser.T__1) self.state = 67 @@ -495,57 +1739,55 @@ def subgraph(self): self.exitRule() return localctx - class VertexContext(ParserRuleContext): + __slots__ = "parser" - def __init__(self, parser, parent:ParserRuleContext=None, invokingState:int=-1): + def __init__( + self, parser, parent: ParserRuleContext = None, invokingState: int = -1 + ): super().__init__(parent, invokingState) self.parser = parser def structuralid(self): - return self.getTypedRuleContext(reggieParser.StructuralidContext,0) - + return self.getTypedRuleContext(reggieParser.StructuralidContext, 0) def labelfilter(self): - return self.getTypedRuleContext(reggieParser.LabelfilterContext,0) - + return self.getTypedRuleContext(reggieParser.LabelfilterContext, 0) def coloringfilter(self): - return self.getTypedRuleContext(reggieParser.ColoringfilterContext,0) - + return self.getTypedRuleContext(reggieParser.ColoringfilterContext, 0) - def structuralvertexpattern(self, i:int=None): + def structuralvertexpattern(self, i: int = None): if i is None: - return self.getTypedRuleContexts(reggieParser.StructuralvertexpatternContext) + return self.getTypedRuleContexts( + reggieParser.StructuralvertexpatternContext + ) else: - return self.getTypedRuleContext(reggieParser.StructuralvertexpatternContext,i) - + return self.getTypedRuleContext( + reggieParser.StructuralvertexpatternContext, i + ) def getRuleIndex(self): return reggieParser.RULE_vertex - def enterRule(self, listener:ParseTreeListener): - if hasattr( listener, "enterVertex" ): + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterVertex"): listener.enterVertex(self) - def exitRule(self, listener:ParseTreeListener): - if hasattr( listener, "exitVertex" ): + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitVertex"): listener.exitVertex(self) - def accept(self, visitor:ParseTreeVisitor): - if hasattr( visitor, "visitVertex" ): + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitVertex"): return visitor.visitVertex(self) else: return visitor.visitChildren(self) - - - def vertex(self): - localctx = reggieParser.VertexContext(self, self._ctx, self.state) self.enterRule(localctx, 10, self.RULE_vertex) - self._la = 0 # Token type + self._la = 0 # Token type try: self.enterOuterAlt(localctx, 1) self.state = 75 @@ -553,25 +1795,30 @@ def vertex(self): self.state = 78 self._errHandler.sync(self) _la = self._input.LA(1) - if _la==reggieParser.T__5: + if _la == reggieParser.T__5: self.state = 76 self.match(reggieParser.T__5) self.state = 77 self.labelfilter() - self.state = 81 self._errHandler.sync(self) _la = self._input.LA(1) - if _la==reggieParser.T__0: + if _la == reggieParser.T__0: self.state = 80 self.coloringfilter() - self.state = 86 self._errHandler.sync(self) _la = self._input.LA(1) - while (((_la) & ~0x3f) == 0 and ((1 << _la) & ((1 << reggieParser.T__6) | (1 << reggieParser.T__8) | (1 << reggieParser.T__9))) != 0): + while ((_la) & ~0x3F) == 0 and ( + (1 << _la) + & ( + (1 << reggieParser.T__6) + | (1 << reggieParser.T__8) + | (1 << reggieParser.T__9) + ) + ) != 0: self.state = 83 self.structuralvertexpattern() self.state = 88 @@ -586,14 +1833,16 @@ def vertex(self): self.exitRule() return localctx - class ColoringfilterContext(ParserRuleContext): + __slots__ = "parser" - def __init__(self, parser, parent:ParserRuleContext=None, invokingState:int=-1): + def __init__( + self, parser, parent: ParserRuleContext = None, invokingState: int = -1 + ): super().__init__(parent, invokingState) self.parser = parser - def STRING(self, i:int=None): + def STRING(self, i: int = None): if i is None: return self.getTokens(reggieParser.STRING) else: @@ -602,28 +1851,24 @@ def STRING(self, i:int=None): def getRuleIndex(self): return reggieParser.RULE_coloringfilter - def enterRule(self, listener:ParseTreeListener): - if hasattr( listener, "enterColoringfilter" ): + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterColoringfilter"): listener.enterColoringfilter(self) - def exitRule(self, listener:ParseTreeListener): - if hasattr( listener, "exitColoringfilter" ): + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitColoringfilter"): listener.exitColoringfilter(self) - def accept(self, visitor:ParseTreeVisitor): - if hasattr( visitor, "visitColoringfilter" ): + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitColoringfilter"): return visitor.visitColoringfilter(self) else: return visitor.visitChildren(self) - - - def coloringfilter(self): - localctx = reggieParser.ColoringfilterContext(self, self._ctx, self.state) self.enterRule(localctx, 12, self.RULE_coloringfilter) - self._la = 0 # Token type + self._la = 0 # Token type try: self.enterOuterAlt(localctx, 1) self.state = 89 @@ -633,7 +1878,7 @@ def coloringfilter(self): self.state = 95 self._errHandler.sync(self) _la = self._input.LA(1) - while _la==reggieParser.T__1: + while _la == reggieParser.T__1: self.state = 91 self.match(reggieParser.T__1) self.state = 92 @@ -652,48 +1897,45 @@ def coloringfilter(self): self.exitRule() return localctx - class StructuralvertexpatternContext(ParserRuleContext): + __slots__ = "parser" - def __init__(self, parser, parent:ParserRuleContext=None, invokingState:int=-1): + def __init__( + self, parser, parent: ParserRuleContext = None, invokingState: int = -1 + ): super().__init__(parent, invokingState) self.parser = parser def intmodifier(self): - return self.getTypedRuleContext(reggieParser.IntmodifierContext,0) - + return self.getTypedRuleContext(reggieParser.IntmodifierContext, 0) def starmodifier(self): - return self.getTypedRuleContext(reggieParser.StarmodifierContext,0) - + return self.getTypedRuleContext(reggieParser.StarmodifierContext, 0) def plusmodifier(self): - return self.getTypedRuleContext(reggieParser.PlusmodifierContext,0) - + return self.getTypedRuleContext(reggieParser.PlusmodifierContext, 0) def getRuleIndex(self): return reggieParser.RULE_structuralvertexpattern - def enterRule(self, listener:ParseTreeListener): - if hasattr( listener, "enterStructuralvertexpattern" ): + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterStructuralvertexpattern"): listener.enterStructuralvertexpattern(self) - def exitRule(self, listener:ParseTreeListener): - if hasattr( listener, "exitStructuralvertexpattern" ): + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitStructuralvertexpattern"): listener.exitStructuralvertexpattern(self) - def accept(self, visitor:ParseTreeVisitor): - if hasattr( visitor, "visitStructuralvertexpattern" ): + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitStructuralvertexpattern"): return visitor.visitStructuralvertexpattern(self) else: return visitor.visitChildren(self) - - - def structuralvertexpattern(self): - - localctx = reggieParser.StructuralvertexpatternContext(self, self._ctx, self.state) + localctx = reggieParser.StructuralvertexpatternContext( + self, self._ctx, self.state + ) self.enterRule(localctx, 14, self.RULE_structuralvertexpattern) try: self.enterOuterAlt(localctx, 1) @@ -723,10 +1965,12 @@ def structuralvertexpattern(self): self.exitRule() return localctx - class IntmodifierContext(ParserRuleContext): + __slots__ = "parser" - def __init__(self, parser, parent:ParserRuleContext=None, invokingState:int=-1): + def __init__( + self, parser, parent: ParserRuleContext = None, invokingState: int = -1 + ): super().__init__(parent, invokingState) self.parser = parser @@ -736,25 +1980,21 @@ def INT(self): def getRuleIndex(self): return reggieParser.RULE_intmodifier - def enterRule(self, listener:ParseTreeListener): - if hasattr( listener, "enterIntmodifier" ): + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterIntmodifier"): listener.enterIntmodifier(self) - def exitRule(self, listener:ParseTreeListener): - if hasattr( listener, "exitIntmodifier" ): + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitIntmodifier"): listener.exitIntmodifier(self) - def accept(self, visitor:ParseTreeVisitor): - if hasattr( visitor, "visitIntmodifier" ): + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitIntmodifier"): return visitor.visitIntmodifier(self) else: return visitor.visitChildren(self) - - - def intmodifier(self): - localctx = reggieParser.IntmodifierContext(self, self._ctx, self.state) self.enterRule(localctx, 16, self.RULE_intmodifier) try: @@ -773,36 +2013,33 @@ def intmodifier(self): self.exitRule() return localctx - class StarmodifierContext(ParserRuleContext): + __slots__ = "parser" - def __init__(self, parser, parent:ParserRuleContext=None, invokingState:int=-1): + def __init__( + self, parser, parent: ParserRuleContext = None, invokingState: int = -1 + ): super().__init__(parent, invokingState) self.parser = parser - def getRuleIndex(self): return reggieParser.RULE_starmodifier - def enterRule(self, listener:ParseTreeListener): - if hasattr( listener, "enterStarmodifier" ): + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterStarmodifier"): listener.enterStarmodifier(self) - def exitRule(self, listener:ParseTreeListener): - if hasattr( listener, "exitStarmodifier" ): + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitStarmodifier"): listener.exitStarmodifier(self) - def accept(self, visitor:ParseTreeVisitor): - if hasattr( visitor, "visitStarmodifier" ): + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitStarmodifier"): return visitor.visitStarmodifier(self) else: return visitor.visitChildren(self) - - - def starmodifier(self): - localctx = reggieParser.StarmodifierContext(self, self._ctx, self.state) self.enterRule(localctx, 18, self.RULE_starmodifier) try: @@ -817,36 +2054,33 @@ def starmodifier(self): self.exitRule() return localctx - class PlusmodifierContext(ParserRuleContext): + __slots__ = "parser" - def __init__(self, parser, parent:ParserRuleContext=None, invokingState:int=-1): + def __init__( + self, parser, parent: ParserRuleContext = None, invokingState: int = -1 + ): super().__init__(parent, invokingState) self.parser = parser - def getRuleIndex(self): return reggieParser.RULE_plusmodifier - def enterRule(self, listener:ParseTreeListener): - if hasattr( listener, "enterPlusmodifier" ): + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterPlusmodifier"): listener.enterPlusmodifier(self) - def exitRule(self, listener:ParseTreeListener): - if hasattr( listener, "exitPlusmodifier" ): + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitPlusmodifier"): listener.exitPlusmodifier(self) - def accept(self, visitor:ParseTreeVisitor): - if hasattr( visitor, "visitPlusmodifier" ): + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitPlusmodifier"): return visitor.visitPlusmodifier(self) else: return visitor.visitChildren(self) - - - def plusmodifier(self): - localctx = reggieParser.PlusmodifierContext(self, self._ctx, self.state) self.enterRule(localctx, 20, self.RULE_plusmodifier) try: @@ -861,10 +2095,12 @@ def plusmodifier(self): self.exitRule() return localctx - class StructuralidContext(ParserRuleContext): + __slots__ = "parser" - def __init__(self, parser, parent:ParserRuleContext=None, invokingState:int=-1): + def __init__( + self, parser, parent: ParserRuleContext = None, invokingState: int = -1 + ): super().__init__(parent, invokingState) self.parser = parser @@ -877,33 +2113,31 @@ def QUESTIONMARK_WILDCARD(self): def getRuleIndex(self): return reggieParser.RULE_structuralid - def enterRule(self, listener:ParseTreeListener): - if hasattr( listener, "enterStructuralid" ): + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterStructuralid"): listener.enterStructuralid(self) - def exitRule(self, listener:ParseTreeListener): - if hasattr( listener, "exitStructuralid" ): + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitStructuralid"): listener.exitStructuralid(self) - def accept(self, visitor:ParseTreeVisitor): - if hasattr( visitor, "visitStructuralid" ): + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitStructuralid"): return visitor.visitStructuralid(self) else: return visitor.visitChildren(self) - - - def structuralid(self): - localctx = reggieParser.StructuralidContext(self, self._ctx, self.state) self.enterRule(localctx, 22, self.RULE_structuralid) - self._la = 0 # Token type + self._la = 0 # Token type try: self.enterOuterAlt(localctx, 1) self.state = 113 _la = self._input.LA(1) - if not(_la==reggieParser.ID or _la==reggieParser.QUESTIONMARK_WILDCARD): + if not ( + _la == reggieParser.ID or _la == reggieParser.QUESTIONMARK_WILDCARD + ): self._errHandler.recoverInline(self) else: self._errHandler.reportMatch(self) @@ -916,45 +2150,42 @@ def structuralid(self): self.exitRule() return localctx - class LabelfilterContext(ParserRuleContext): + __slots__ = "parser" - def __init__(self, parser, parent:ParserRuleContext=None, invokingState:int=-1): + def __init__( + self, parser, parent: ParserRuleContext = None, invokingState: int = -1 + ): super().__init__(parent, invokingState) self.parser = parser - def label(self, i:int=None): + def label(self, i: int = None): if i is None: return self.getTypedRuleContexts(reggieParser.LabelContext) else: - return self.getTypedRuleContext(reggieParser.LabelContext,i) - + return self.getTypedRuleContext(reggieParser.LabelContext, i) def getRuleIndex(self): return reggieParser.RULE_labelfilter - def enterRule(self, listener:ParseTreeListener): - if hasattr( listener, "enterLabelfilter" ): + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterLabelfilter"): listener.enterLabelfilter(self) - def exitRule(self, listener:ParseTreeListener): - if hasattr( listener, "exitLabelfilter" ): + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitLabelfilter"): listener.exitLabelfilter(self) - def accept(self, visitor:ParseTreeVisitor): - if hasattr( visitor, "visitLabelfilter" ): + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitLabelfilter"): return visitor.visitLabelfilter(self) else: return visitor.visitChildren(self) - - - def labelfilter(self): - localctx = reggieParser.LabelfilterContext(self, self._ctx, self.state) self.enterRule(localctx, 24, self.RULE_labelfilter) - self._la = 0 # Token type + self._la = 0 # Token type try: self.state = 134 self._errHandler.sync(self) @@ -966,7 +2197,7 @@ def labelfilter(self): self.state = 120 self._errHandler.sync(self) _la = self._input.LA(1) - while _la==reggieParser.T__10: + while _la == reggieParser.T__10: self.state = 116 self.match(reggieParser.T__10) self.state = 117 @@ -985,7 +2216,7 @@ def labelfilter(self): self.state = 129 self._errHandler.sync(self) _la = self._input.LA(1) - while _la==reggieParser.T__10: + while _la == reggieParser.T__10: self.state = 125 self.match(reggieParser.T__10) self.state = 126 @@ -1008,10 +2239,12 @@ def labelfilter(self): self.exitRule() return localctx - class LabelContext(ParserRuleContext): + __slots__ = "parser" - def __init__(self, parser, parent:ParserRuleContext=None, invokingState:int=-1): + def __init__( + self, parser, parent: ParserRuleContext = None, invokingState: int = -1 + ): super().__init__(parent, invokingState) self.parser = parser @@ -1021,25 +2254,21 @@ def ID(self): def getRuleIndex(self): return reggieParser.RULE_label - def enterRule(self, listener:ParseTreeListener): - if hasattr( listener, "enterLabel" ): + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterLabel"): listener.enterLabel(self) - def exitRule(self, listener:ParseTreeListener): - if hasattr( listener, "exitLabel" ): + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitLabel"): listener.exitLabel(self) - def accept(self, visitor:ParseTreeVisitor): - if hasattr( visitor, "visitLabel" ): + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitLabel"): return visitor.visitLabel(self) else: return visitor.visitChildren(self) - - - def label(self): - localctx = reggieParser.LabelContext(self, self._ctx, self.state) self.enterRule(localctx, 26, self.RULE_label) try: @@ -1054,52 +2283,48 @@ def label(self): self.exitRule() return localctx - class Vertex2vertexContext(ParserRuleContext): + __slots__ = "parser" - def __init__(self, parser, parent:ParserRuleContext=None, invokingState:int=-1): + def __init__( + self, parser, parent: ParserRuleContext = None, invokingState: int = -1 + ): super().__init__(parent, invokingState) self.parser = parser - def vertex(self, i:int=None): + def vertex(self, i: int = None): if i is None: return self.getTypedRuleContexts(reggieParser.VertexContext) else: - return self.getTypedRuleContext(reggieParser.VertexContext,i) - + return self.getTypedRuleContext(reggieParser.VertexContext, i) - def edge(self, i:int=None): + def edge(self, i: int = None): if i is None: return self.getTypedRuleContexts(reggieParser.EdgeContext) else: - return self.getTypedRuleContext(reggieParser.EdgeContext,i) - + return self.getTypedRuleContext(reggieParser.EdgeContext, i) def getRuleIndex(self): return reggieParser.RULE_vertex2vertex - def enterRule(self, listener:ParseTreeListener): - if hasattr( listener, "enterVertex2vertex" ): + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterVertex2vertex"): listener.enterVertex2vertex(self) - def exitRule(self, listener:ParseTreeListener): - if hasattr( listener, "exitVertex2vertex" ): + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitVertex2vertex"): listener.exitVertex2vertex(self) - def accept(self, visitor:ParseTreeVisitor): - if hasattr( visitor, "visitVertex2vertex" ): + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitVertex2vertex"): return visitor.visitVertex2vertex(self) else: return visitor.visitChildren(self) - - - def vertex2vertex(self): - localctx = reggieParser.Vertex2vertexContext(self, self._ctx, self.state) self.enterRule(localctx, 28, self.RULE_vertex2vertex) - self._la = 0 # Token type + self._la = 0 # Token type try: self.enterOuterAlt(localctx, 1) self.state = 138 @@ -1107,7 +2332,7 @@ def vertex2vertex(self): self.state = 144 self._errHandler.sync(self) _la = self._input.LA(1) - while _la==reggieParser.T__11 or _la==reggieParser.T__12: + while _la == reggieParser.T__11 or _la == reggieParser.T__12: self.state = 139 self.edge() self.state = 140 @@ -1124,44 +2349,41 @@ def vertex2vertex(self): self.exitRule() return localctx - class EdgeContext(ParserRuleContext): + __slots__ = "parser" - def __init__(self, parser, parent:ParserRuleContext=None, invokingState:int=-1): + def __init__( + self, parser, parent: ParserRuleContext = None, invokingState: int = -1 + ): super().__init__(parent, invokingState) self.parser = parser - def getRuleIndex(self): return reggieParser.RULE_edge - def enterRule(self, listener:ParseTreeListener): - if hasattr( listener, "enterEdge" ): + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterEdge"): listener.enterEdge(self) - def exitRule(self, listener:ParseTreeListener): - if hasattr( listener, "exitEdge" ): + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitEdge"): listener.exitEdge(self) - def accept(self, visitor:ParseTreeVisitor): - if hasattr( visitor, "visitEdge" ): + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitEdge"): return visitor.visitEdge(self) else: return visitor.visitChildren(self) - - - def edge(self): - localctx = reggieParser.EdgeContext(self, self._ctx, self.state) self.enterRule(localctx, 30, self.RULE_edge) - self._la = 0 # Token type + self._la = 0 # Token type try: self.enterOuterAlt(localctx, 1) self.state = 147 _la = self._input.LA(1) - if not(_la==reggieParser.T__11 or _la==reggieParser.T__12): + if not (_la == reggieParser.T__11 or _la == reggieParser.T__12): self._errHandler.recoverInline(self) else: self._errHandler.reportMatch(self) @@ -1173,8 +2395,3 @@ def edge(self): finally: self.exitRule() return localctx - - - - - diff --git a/lfr/antlrgen/reggie/reggieVisitor.py b/lfr/antlrgen/reggie/reggieVisitor.py index e90b859..69c8219 100644 --- a/lfr/antlrgen/reggie/reggieVisitor.py +++ b/lfr/antlrgen/reggie/reggieVisitor.py @@ -1,16 +1,15 @@ -# Generated from /Users/krishna/CIDAR/reggie/reggie.g4 by ANTLR 4.9 +# Generated from ./reggie.g4 by ANTLR 4.10.1 from antlr4 import * if __name__ is not None and "." in __name__: from .reggieParser import reggieParser else: - from lfr.graphmatchParser import reggieParser + from reggieParser import reggieParser # This class defines a complete generic visitor for a parse tree produced by reggieParser. class reggieVisitor(ParseTreeVisitor): - # Visit a parse tree produced by reggieParser#graph. def visitGraph(self, ctx: reggieParser.GraphContext): return self.visitChildren(ctx) @@ -78,4 +77,4 @@ def visitEdge(self, ctx: reggieParser.EdgeContext): return self.visitChildren(ctx) -del reggieParser \ No newline at end of file +del reggieParser diff --git a/lfr/api.py b/lfr/api.py new file mode 100644 index 0000000..61b86e7 --- /dev/null +++ b/lfr/api.py @@ -0,0 +1,192 @@ +import os +from pathlib import Path +from typing import List, Union + +from antlr4 import CommonTokenStream, FileStream, ParseTreeWalker + +from lfr import parameters +from lfr.antlrgen.lfr.lfrXLexer import lfrXLexer +from lfr.antlrgen.lfr.lfrXParser import lfrXParser +from lfr.moduleinstanceListener import ModuleInstanceListener +from lfr.netlistgenerator.generator import generate +from lfr.netlistgenerator.mappinglibrary_generator import ( + generate_dropx_library, + generate_mars_library, + generate_mlsi_library, +) +from lfr.parameters import OUTPUT_DIR, PREPROCESSOR_DUMP_FILE_NAME +from lfr.postProcessListener import PostProcessListener +from lfr.preprocessor import PreProcessor +from lfr.utils import print_netlist, printgraph, serialize_netlist + + +def run_preprocessor( + input_files: List[str], + pre_load: List[str] = [], + preprocessor_dump_input_path: Path = Path(PREPROCESSOR_DUMP_FILE_NAME).resolve(), +) -> bool: + """Runs the preprocessor on the input files + + Args: + input_files (List[str]): input files to be preprocessed + pre_load (List[str], optional): Preload Directory. Defaults to []. + + Returns: + bool: True if the preprocessor ran successfully, False otherwise + """ + pre_load_file_list = pre_load + print(pre_load_file_list) + + # Utilize the prepreocessor to generate the input file + preprocessor = PreProcessor(input_files, pre_load_file_list) + + if preprocessor.check_syntax_errors(): + print("Stopping compiler because of syntax errors") + return False + + preprocessor.process(preprocessor_dump_input_path) + return True + + +def synthesize_module( + input_path: Path = Path(PREPROCESSOR_DUMP_FILE_NAME).resolve(), + no_annotations_flag: bool = False, + print_fig: bool = True, +) -> Union[ModuleInstanceListener, PostProcessListener]: + """Generates the module from the preprocessor dump + + This is the method you want to use if you want to get module/fluid interaction graph + + Args: + preprocessor_dump_input_path (Path, optional): Location of the preprocessor dump. Defaults to Path(PREPROCESSOR_DUMP_FILE_NAME).resolve(). + no_annotations_flag (bool, optional): Skips parsing annotations. Defaults to False. + + Returns: + Union[ModuleInstanceListener, PostProcessListener]: Returns the object model for the overall device module + """ + # Modifiy this to translate relative path to absolute path in the future + finput = FileStream(str(input_path)) + + lexer = lfrXLexer(finput) + + stream = CommonTokenStream(lexer) + + parser = lfrXParser(stream) + + tree = parser.skeleton() + + walker = ParseTreeWalker() + + if no_annotations_flag is True: + mapping_listener = ModuleInstanceListener() + else: + mapping_listener = PostProcessListener() + + walker.walk(mapping_listener, tree) + + mapping_listener.print_stack() + + mapping_listener.print_variables() + + if mapping_listener.currentModule is not None: + interactiongraph = mapping_listener.currentModule.FIG + if print_fig is True: + printgraph(interactiongraph, mapping_listener.currentModule.name + ".dot") + + return mapping_listener + + +def compile_lfr( + input_files: List[str], + outpath: str = "./out/", + technology: str = "dropx", + library_path: str = "./library", + no_mapping_flag: bool = False, + no_gen_flag: bool = False, + no_annotations_flag: bool = False, + pre_load: List[str] = [], +) -> int: + """Standard API to compile a lfr file + + This is the hook that we use for running lfr files from the command line or in + programs. Assumes that all the paths for the input files exist. It can create + the output directories if they aren't present. + + Args: + input_files (List[str]): The paths for the input lfr files + outpath (str, optional): The path where all the outputs are saved. Defaults to "out/". + technology (str, optional): String for library that we need to use. Defaults to "dropx". + library_path (str, optional): path where all the library files are placed. Defaults to "./library". + no_mapping_flag (bool, optional): Enables/Disables mapping. Defaults to False. + no_gen_flag (bool, optional): Enables/Disables device generation. Defaults to False. + no_annotations_flag (bool, optional): Skip Annotation parsing. Defaults to False. + pre_load (List[str], optional): Preload Directory. Defaults to []. + + Raises: + ValueError: _description_ + ValueError: _description_ + + Returns: + int: 0 if the compilation was successful, >0 if theres an error + """ + preprocessor_dump_rel_input_path = PREPROCESSOR_DUMP_FILE_NAME + preprocessor_dump_input_path = Path(preprocessor_dump_rel_input_path).resolve() + + preprocessor_success = run_preprocessor( + input_files, pre_load, preprocessor_dump_input_path + ) + + if preprocessor_success is False: + return 1 + + print("output dir:", outpath) + print(input_files) + + abspath = Path(outpath).absolute() + parameters.OUTPUT_DIR = abspath + + if os.path.isdir(abspath) is not True: + print("Creating the output directory:") + path = Path(parameters.OUTPUT_DIR) + path.mkdir(parents=True) + + library = None + # library = libraries[library_name] + + # Setup and run the compiler's mapping listener + mapping_listener = synthesize_module( + preprocessor_dump_input_path, no_annotations_flag + ) + + if no_gen_flag is True: + return 0 + + # Check if the module compilation was successful + if mapping_listener.success: + # Now Process the Modules Generated + # V2 generator + if technology == "dropx": + library = generate_dropx_library() + elif technology == "mars": + library = generate_mars_library() + elif technology == "mlsi": + library = generate_mlsi_library() + else: + print("Implement Library for whatever else") + raise NotImplementedError( + f"No implementation found library for technology: {technology}" + ) + + if mapping_listener.currentModule is None: + raise ValueError() + if library is None: + raise ValueError() + unsized_devices = generate(mapping_listener.currentModule, library) + + for index, unsized_device in enumerate(unsized_devices): + output_path = Path(OUTPUT_DIR).joinpath(f"variant_{index}") + output_path.mkdir(parents=True, exist_ok=True) + print_netlist(output_path, unsized_device) + serialize_netlist(output_path, unsized_device) + + return 0 diff --git a/lfr/cmdline.py b/lfr/cmdline.py index 41cc6f0..43486ad 100644 --- a/lfr/cmdline.py +++ b/lfr/cmdline.py @@ -1,27 +1,13 @@ import argparse import glob import json -from lfr.postProcessListener import PostProcessListener import os -import sys -from pathlib import Path -from antlr4 import CommonTokenStream, FileStream, ParseTreeWalker +from art import tprint import lfr.parameters as parameters -from lfr.moduleinstanceListener import ModuleInstanceListener -from lfr.antlrgen.lfr.lfrXLexer import lfrXLexer -from lfr.antlrgen.lfr.lfrXParser import lfrXParser +from lfr.api import compile_lfr from lfr.netlistgenerator.mappinglibrary import MappingLibrary -from lfr.netlistgenerator.generator import ( - generate, - generate_dropx_library, - generate_mars_library, - generate_mlsi_library, -) -from lfr.utils import print_netlist, printgraph, serialize_netlist -from lfr.preprocessor import PreProcessor -from art import tprint def load_libraries(): @@ -84,86 +70,26 @@ def main(): ) args = parser.parse_args() - pre_load_file_list = args.pre_load - # Utilize the prepreocessor to generate the input file - preprocessor = PreProcessor(args.input, pre_load_file_list) - - if preprocessor.check_syntax_errors(): - print("Stopping compiler because of syntax errors") - sys.exit(0) - - preprocessor.process() - - print("output dir:", args.outpath) - print(args.input) - - rel_input_path = "pre_processor_dump.lfr" - input_path = Path(rel_input_path).resolve() - - abspath = os.path.abspath(args.outpath) - parameters.OUTPUT_DIR = abspath - - if os.path.isdir(abspath) is not True: - print("Creating the output directory:") - path = Path(parameters.OUTPUT_DIR) - path.mkdir(parents=True) - - library = None - # library = libraries[library_name] - - # Modifiy this to translate relative path to absolute path in the future - finput = FileStream(str(input_path)) - - lexer = lfrXLexer(finput) - - stream = CommonTokenStream(lexer) - - parser = lfrXParser(stream) - - tree = parser.skeleton() - - walker = ParseTreeWalker() - - if args.no_annotations is True: - mapping_listener = ModuleInstanceListener() - else: - mapping_listener = PostProcessListener() - - walker.walk(mapping_listener, tree) - - mapping_listener.print_stack() - - mapping_listener.print_variables() - - if mapping_listener.currentModule is not None: - interactiongraph = mapping_listener.currentModule.FIG - printgraph(interactiongraph, mapping_listener.currentModule.name + ".dot") - - if args.no_gen is True: - sys.exit(0) - - # Check if the module compilation was successful - if mapping_listener.success: - # Now Process the Modules Generated - # V2 generator - if args.technology == "dropx": - library = generate_dropx_library() - elif args.technology == "mars": - library = generate_mars_library() - elif args.technology == "mlsi": - library = generate_mlsi_library() - else: - print("Implement Library for whatever else") - pass - - if mapping_listener.currentModule is None: - raise ValueError() - if library is None: - raise ValueError() - unsized_device = generate(mapping_listener.currentModule, library) - - print_netlist(unsized_device) - serialize_netlist(unsized_device) + # Generate proxy variables for the parsed args + input_files = args.input + outpath = args.outpath + technology = args.technology + library_path = args.library + no_mapping_flag = args.no_mapping + no_gen_flag = args.no_gen + no_annotations_flag = args.no_annotations + pre_load = args.pre_load + + compile_lfr( + input_files=input_files, + outpath=outpath, + technology=technology, + library_path=library_path, + no_mapping_flag=no_mapping_flag, + no_gen_flag=no_gen_flag, + no_annotations_flag=no_annotations_flag, + pre_load=pre_load, + ) if __name__ == "__main__": diff --git a/lfr/compiler/distribute/distributeblock.py b/lfr/compiler/distribute/distributeblock.py index a740e27..7fe2577 100644 --- a/lfr/compiler/distribute/distributeblock.py +++ b/lfr/compiler/distribute/distributeblock.py @@ -1,8 +1,7 @@ -from typing import List +from typing import List, Optional from lfr.compiler.distribute.BitVector import BitVector from lfr.compiler.distribute.statetable import StateTable -from typing import List, Optional from lfr.compiler.language.vectorrange import VectorRange from lfr.fig.fluidinteractiongraph import FluidInteractionGraph diff --git a/lfr/compiler/distribute/statetable.py b/lfr/compiler/distribute/statetable.py index 6a15468..bffc6eb 100644 --- a/lfr/compiler/distribute/statetable.py +++ b/lfr/compiler/distribute/statetable.py @@ -1,11 +1,12 @@ from __future__ import annotations -from typing import TYPE_CHECKING + +from typing import TYPE_CHECKING, Optional, Union + +from lfr.fig.fignode import FIGNode if TYPE_CHECKING: from lfr.fig.fluidinteractiongraph import FluidInteractionGraph -from lfr.utils import convert_list_to_str -from lfr.fig.annotation import ANDAnnotation, NOTAnnotation, ORAnnotation from typing import Dict, List, Tuple import networkx as nx @@ -13,6 +14,13 @@ from tabulate import tabulate from lfr.compiler.distribute.BitVector import BitVector +from lfr.fig.annotation import ( + ANDAnnotation, + DistributeAnnotation, + NOTAnnotation, + ORAnnotation, +) +from lfr.utils import convert_list_to_str class StateTable: @@ -26,7 +34,7 @@ def __init__(self, signal_list: List[str]) -> None: self._connectivity_matrix = np.zeros((1, 1)) self._control_matrix = np.zeros((1, 1)) self._connectivity_column_headers: List[str] = [] - self._connectivity_edges = {} + self._connectivity_edges: Dict[str, Tuple[str, str]] = {} self._and_annotations: List[ANDAnnotation] = [] self._or_annotations: List[ORAnnotation] = [] self._not_annotations: List[NOTAnnotation] = [] @@ -84,8 +92,7 @@ def save_connectivity( digraph.add_edge(source, target) def generate_connectivity_table(self) -> None: - - connectivy_column_headers = [] + connectivy_column_headers: List[str] = [] self._connectivity_column_headers = connectivy_column_headers # First setup the dimensions for the matrix row_size = len(self._connectivity_states.keys()) @@ -203,7 +210,6 @@ def generate_and_annotations(self, fig: FluidInteractionGraph) -> None: self._and_annotations.append(annotation) def generate_or_annotations(self, fig: FluidInteractionGraph) -> None: - self.print_connectivity_table() m = np.copy(self._connectivity_matrix) # Zerofill SKIPPED COLUMS @@ -263,7 +269,9 @@ def generate_or_annotations(self, fig: FluidInteractionGraph) -> None: # else (is not present in AND annotation) pick up the positive's corresponding # flow node as one of the targets for the or annotation for candidate in all_candidates: - args_for_annotation = [] + args_for_annotation: List[ + Union[Tuple[FIGNode, FIGNode], DistributeAnnotation] + ] = [] for row_index in candidate: row = m[row_index, :] for i in range(len(row)): @@ -291,10 +299,9 @@ def generate_or_annotations(self, fig: FluidInteractionGraph) -> None: annotation_to_use = None for annotation in self._and_annotations: if source in annotation.get_items(): - found_flag = True annotation_to_use = annotation break - if found_flag is True: + if annotation_to_use is not None: if annotation_to_use not in args_for_annotation: args_for_annotation.append(annotation_to_use) else: diff --git a/lfr/compiler/language/concatenation.py b/lfr/compiler/language/concatenation.py index 175baef..3d9ad8a 100644 --- a/lfr/compiler/language/concatenation.py +++ b/lfr/compiler/language/concatenation.py @@ -1,9 +1,22 @@ +from __future__ import annotations + +from typing import List, Optional + +from lfr.compiler.language.vector import Vector from lfr.compiler.language.vectorrange import VectorRange class Concatenation: - def __init__(self, ranges=None): - self.ranges = [] + """ + A concatenation is the represetnation of mutiple + vector ranges stitched togeter. + + TODO - A concatenation of concatenations should be a concatenation, + while this needs to be tested more thoroughly, this is future work. + """ + + def __init__(self, ranges: List[VectorRange]): + self.ranges: List[VectorRange] = [] if ranges is not None: self.ranges = ranges name = "CONCAT" @@ -30,12 +43,18 @@ def __len__(self): size = size + len(vrange) return size - def get_range(self, startindex: int = None, endindex: int = None) -> VectorRange: - start = startindex if startindex is not None else 0 + def get_range( + self, startindex: int = 0, endindex: Optional[int] = None + ) -> VectorRange: + start = startindex end = endindex if endindex is not None else len(self) - 1 - # vec = [] - # for r in self.ranges: - # vec.extend(r) - ret = VectorRange(self, start, end) + concatenated_data = [] + for data in self.ranges: + concatenated_data.extend(data) + concatenated_vector = Vector( + self.id, + ) + concatenated_vector.vec = concatenated_data + ret = VectorRange(concatenated_vector, start, end) return ret diff --git a/lfr/compiler/language/fluidexpression.py b/lfr/compiler/language/fluidexpression.py index 5ea7e8e..33b40e7 100644 --- a/lfr/compiler/language/fluidexpression.py +++ b/lfr/compiler/language/fluidexpression.py @@ -21,7 +21,6 @@ def __init__(self, module: Module) -> None: def process_expression( self, termlist: List[Union[VectorRange, float]], operatorlist: List[str] ): - # In step1, we go over and complete all the numeric operations in the # precedence of numeric operation order. It is possible that there are no # numeric operations going on in the expression. In that case we have the @@ -143,7 +142,7 @@ def process_unary_operation(self, term, operator): result.append(interaction) v = Vector.create_from_list_things(operator, result) - ret = v.get_range() + ret = VectorRange.get_range_from_vector(v) return ret def __evalute_fluid_fluid_operator( @@ -226,12 +225,11 @@ def __evalute_fluid_fluid_operator( result.append(result_element) v = Vector.create_from_list_things(vecname, result) - return v.get_range() + return VectorRange.get_range_from_vector(v) def __evaluate_fluid_numeric_operator( self, operand_fluidic: VectorRange, operand_numeric: float, operator: str ) -> VectorRange: - fluid = operand_fluidic[0] interactions = [] for fluid in operand_fluidic: @@ -275,7 +273,7 @@ def __evaluate_fluid_numeric_operator( v = Vector.create_from_list_things( "interaction_" + operand_fluidic.id, interactions ) - result = v.get_range() + result = VectorRange.get_range_from_vector(v) return result @staticmethod diff --git a/lfr/compiler/language/vector.py b/lfr/compiler/language/vector.py index c4d7870..8acf596 100644 --- a/lfr/compiler/language/vector.py +++ b/lfr/compiler/language/vector.py @@ -1,40 +1,45 @@ -from typing import List -from lfr.compiler.language.vectorrange import VectorRange +from typing import Generic, List, Optional, Type, TypeVar +T = TypeVar("T") + + +class Vector(Generic[T]): + """ + Vector of objects T + """ -class Vector: def __init__( - self, id: str, vectortype=None, startindex: int = 0, endindex: int = 0 + self, + id: str, + vector_type: Optional[Type[T]] = None, + startindex: int = 0, + endindex: int = -1, ): self.id = id self.startindex = startindex self.endindex = endindex - self.vec = [] + self.vec: List[T] = [] - if vectortype is not None: - # If its a singular item avoid the indexing + if vector_type is not None: + # If it's a singular item avoid the indexing if len(self) == 1: - self.vec.append(vectortype(self.id)) + self.vec.append(vector_type(self.id)) else: for i in range(len(self)): - self.vec.append(vectortype(self.id + "_" + str(i))) + self.vec.append(vector_type(self.id + "_" + str(i))) + + self.endindex = len(self.vec) - 1 + else: print("Creating a vector of type [None]") - def __len__(self): + def __len__(self) -> int: return abs(self.startindex - self.endindex) + 1 - def get_items(self) -> list: + def get_items(self) -> List[T]: return self.vec - def get_range(self, startindex: int = None, endindex: int = None) -> VectorRange: - start = startindex if startindex is not None else self.startindex - end = endindex if endindex is not None else self.endindex - ret = VectorRange(self, start, end) - - return ret - - def __getitem__(self, key: int): + def __getitem__(self, key): if isinstance(key, slice): start, stop, step = key.indices(len(self.vec)) return [self.vec[i] for i in range(start, stop, step)] diff --git a/lfr/compiler/language/vectorrange.py b/lfr/compiler/language/vectorrange.py index c6b05f7..cfde0d0 100644 --- a/lfr/compiler/language/vectorrange.py +++ b/lfr/compiler/language/vectorrange.py @@ -1,29 +1,35 @@ -from networkx.algorithms.operators.unary import reverse +from __future__ import annotations -from typing import TYPE_CHECKING +from typing import TYPE_CHECKING, Generic, Optional, TypeVar if TYPE_CHECKING: from lfr.compiler.language.vector import Vector +T = TypeVar("T") -class VectorRange: - def __init__(self, vector, startindex: int, endindex: int): - self.vector: Vector = vector - if startindex is None: - self.startindex = 0 - else: - self.startindex = startindex - if endindex is None: - self.endindex = len(self.vector) - 1 +class VectorRange(Generic[T]): + """ + Vector Range is akin to a slice that you can use to navigate a vector + """ + + def __init__(self, vector: Vector[T], startindex: int = 0, endindex: int = -1): + self.vector: Vector[T] = vector + + self.startindex = startindex + + if endindex < 0: + self.endindex = len(self.vector) + endindex else: self.endindex = endindex @property - def id(self): + def id(self) -> str: return self.vector.id def __getitem__(self, key): + if isinstance(key, slice): + raise NotImplementedError("Need to implement the slice") if self.startindex <= self.endindex: return self.vector[self.startindex + key] else: @@ -33,12 +39,34 @@ def __iter__(self): if self.startindex <= self.endindex: return iter(self.vector[self.startindex : self.endindex + 1]) else: - return iter(reverse(self.vector[self.startindex : self.endindex + 1])) + return reversed((self.vector[self.startindex : self.endindex + 1])) - def __len__(self): + def __len__(self) -> int: return abs(self.startindex - self.endindex) + 1 - def __str__(self): - return "< VectorRange : {0} [{1} : {2}]>".format( + def __str__(self) -> str: + return "".format( self.vector.id, self.startindex, self.endindex ) + + @staticmethod + def get_range_from_vector( + vector: Vector[T], + startindex: Optional[int] = None, + endindex: Optional[int] = None, + ) -> VectorRange: + """Returns a VectorRange from a vector + + Args: + vector (Vector[T]): Vector that want to be able to go through + startindex (Optional[int], optional): the start index of the range. Defaults to None. + endindex (Optional[int], optional): the end index of the range. Defaults to None. + + Returns: + VectorRange: Vector range for the given indices + """ + start = startindex if startindex is not None else vector.startindex + end = endindex if endindex is not None else vector.endindex + ret = VectorRange(vector, start, end) + + return ret diff --git a/lfr/compiler/module.py b/lfr/compiler/module.py index a7d587f..a64b120 100644 --- a/lfr/compiler/module.py +++ b/lfr/compiler/module.py @@ -2,6 +2,7 @@ import copy from typing import Dict, List, Optional, Union + from lfr.compiler.moduleio import ModuleIO from lfr.fig.fignode import FIGNode, Flow, IONode, IOType from lfr.fig.fluidinteractiongraph import FluidInteractionGraph @@ -115,7 +116,6 @@ def add_finteraction_custom_interaction( def add_fluid_fluid_interaction( self, fluid1: Flow, fluid2: Flow, interaction_type: InteractionType ) -> Interaction: - fluid_interaction = FluidFluidInteraction(fluid1, fluid2, interaction_type) self.FIG.add_interaction(fluid_interaction) @@ -161,14 +161,30 @@ def add_fluid_numeric_interaction( number: Union[int, float], interaction_type: InteractionType, ) -> Interaction: - # finteraction = FluidInteraction(fluid1=fluid1, interactiontype=interaction) - finteraction = None + """Add a fluid numeric interaction to the module + + Args: + fluid1 (Flow): Fluid to interact with + number (Union[int, float]): Number to interact with + interaction_type (InteractionType): Type of interaction + + Raises: + NotImplementedError: Currently not supporting variables and their lookups + ValueError: If the interaction type is not supported + + Returns: + Interaction: The interaction that was added + """ + + finteraction: Union[FluidIntegerInteraction, FluidNumberInteraction] if interaction_type is InteractionType.METER: finteraction = FluidNumberInteraction(fluid1, number, interaction_type) elif interaction_type is InteractionType.DILUTE: if isinstance(number, float): finteraction = FluidNumberInteraction(fluid1, number, interaction_type) + elif isinstance(number, int): + raise ValueError("Dilute interaction only supports float values") else: # If its a variable get the corresponding value for it # from the variable store @@ -176,12 +192,14 @@ def add_fluid_numeric_interaction( elif interaction_type is InteractionType.DIVIDE: if isinstance(number, int): finteraction = FluidIntegerInteraction(fluid1, number, interaction_type) + elif isinstance(number, float): + raise ValueError("Divide interaction only supports integer values") else: # If its a variable get the corresponding value for it # from the variable store raise NotImplementedError() else: - raise Exception("Unsupported Numeric Operator") + raise ValueError(f"Unsupported Numeric Operator: {interaction_type}") self.FIG.add_interaction(finteraction) @@ -206,19 +224,20 @@ def instantiate_module( # Step 2 - Create a copy of the fig if module_to_import is None: raise ReferenceError("module_to_import is set to none") - fig_copy = copy.deepcopy(module_to_import.FIG) + + fig_copy: FluidInteractionGraph = copy.deepcopy(module_to_import.FIG) # Step 3 - Convert all the flow IO nodes where mappings exist # to flow nodes for there_node_key in io_mapping.keys(): fignode = fig_copy.get_fignode(there_node_key) # Skip if its a control type one - if isinstance(fignode, IONode) is False: + if isinstance(fignode, IONode) is True: + if fignode.type is IOType.CONTROL: # type: ignore + continue + else: raise TypeError("Node not of type IO Node") - if fignode.type is IOType.CONTROL: - continue - # Convert this node into a flow node # Replace new_fignode = Flow(fignode.ID) diff --git a/lfr/compiler/moduleio.py b/lfr/compiler/moduleio.py index 378cc15..4fcfb15 100644 --- a/lfr/compiler/moduleio.py +++ b/lfr/compiler/moduleio.py @@ -1,12 +1,14 @@ +from typing import Optional + from lfr.compiler.language.vectorrange import VectorRange from lfr.fig.fignode import IOType class ModuleIO: - def __init__(self, name: str, iotype: IOType = None): + def __init__(self, name: str, iotype: IOType): self.type = iotype self._id = name - self._vector_ref = None + self._vector_ref: Optional[VectorRange] = None @property def id(self) -> str: @@ -14,7 +16,11 @@ def id(self) -> str: @property def vector_ref(self) -> VectorRange: - assert self._vector_ref is not None + if self._vector_ref is None: + raise ValueError( + f"Vector Reference is not set for ModuleIO: {self.id}, type:" + f" {self.type}" + ) return self._vector_ref @vector_ref.setter diff --git a/lfr/distBlockListener.py b/lfr/distBlockListener.py index 43a2857..679efc1 100644 --- a/lfr/distBlockListener.py +++ b/lfr/distBlockListener.py @@ -1,10 +1,11 @@ -from typing import List, Tuple, Optional -from lfr.compiler.lfrerror import ErrorType, LFRError -from lfr.compiler.distribute.distributeblock import DistributeBlock +from typing import List, Optional, Tuple + from lfr.antlrgen.lfr.lfrXParser import lfrXParser -from lfr.lfrbaseListener import LFRBaseListener, ListenerMode -from lfr.compiler.language.vectorrange import VectorRange from lfr.compiler.distribute.BitVector import BitVector +from lfr.compiler.distribute.distributeblock import DistributeBlock +from lfr.compiler.language.vectorrange import VectorRange +from lfr.compiler.lfrerror import ErrorType, LFRError +from lfr.lfrbaseListener import LFRBaseListener, ListenerMode class DistBlockListener(LFRBaseListener): @@ -192,7 +193,6 @@ def exitElseBlock(self, ctx: lfrXParser.ElseBlockContext): ) for state in remaining_states: for connectivity in self._current_connectivities: - self._current_dist_block.set_connectivity( state, connectivity[0], connectivity[1] ) diff --git a/lfr/fig/annotation.py b/lfr/fig/annotation.py index d909407..82eec3e 100644 --- a/lfr/fig/annotation.py +++ b/lfr/fig/annotation.py @@ -1,5 +1,7 @@ from __future__ import annotations + from typing import List, Tuple, Union + from lfr.fig.fignode import FIGNode diff --git a/lfr/fig/autocomplete.py b/lfr/fig/autocomplete.py new file mode 100644 index 0000000..5003a9c --- /dev/null +++ b/lfr/fig/autocomplete.py @@ -0,0 +1,8 @@ +from lfr.fig.fluidinteractiongraph import FluidInteractionGraph + + +def connect_orphan_IO(fig: FluidInteractionGraph) -> None: + # Step 1 - Go through all the flow nodes and check to see if any of them have zero outputs. + # Step 2 - If they do then generate a new IO node and connect it to the flow node. + + raise NotImplementedError() diff --git a/lfr/fig/fignode.py b/lfr/fig/fignode.py index f281621..0ea078d 100644 --- a/lfr/fig/fignode.py +++ b/lfr/fig/fignode.py @@ -1,4 +1,5 @@ from enum import Enum +from typing import Optional MATCH_STRING_ORDERING = [ "IO", @@ -18,15 +19,37 @@ class IOType(Enum): class FIGNode: + """The fundamental unit of the fluid interaction graph + + All the fignodes are build on top of this. + """ + def __init__(self, id: str) -> None: + """Creates a new instance of FIGNode + + Args: + id (str): unique ID of the fignode + """ self._id: str = id @property - def ID(self): + def ID(self) -> str: + """Returns the ID of the fignode + + Returns: + str: ID of the fignode + """ return self._id @property def match_string(self): + """Returns the Match String + + For the fig node the match string is "-" + + Returns: + _type_: _description_ + """ return "-" def __str__(self) -> str: @@ -39,6 +62,11 @@ def __eq__(self, other): return False def rename(self, id: str) -> None: + """Renames the ID of the fignode + + Args: + id (str): the new ID for the fignode + """ self._id = id def __hash__(self) -> int: @@ -46,16 +74,36 @@ def __hash__(self) -> int: class ValueNode(FIGNode): + """FIGNodes that carries the value that you + use to operate on the fluid interaction graph + """ + def __init__(self, id: str, val: float) -> None: + """Creates a new instance of ValueNode + + Args: + id (str): ID of the value node + val (float): value that we need to store on the node + """ super(ValueNode, self).__init__(id) self._value = val @property - def value(self): + def value(self) -> float: + """Returns the value stored on the node + + Returns: + float: value stored on the node + """ return self._value @property - def match_string(self): + def match_string(self) -> str: + """Returns the match string + + Returns: + str: the value fo the match_string + """ return "VALUE" def __str__(self) -> str: @@ -63,11 +111,26 @@ def __str__(self) -> str: class Flow(FIGNode): - def __init__(self, id) -> None: + """ + Flow node is the node used represent fluids flowing around + node that we use + """ + + def __init__(self, id: str) -> None: + """Creates a new instance of Flow + + Args: + id (str): id of the flow node + """ super(Flow, self).__init__(id) @property def match_string(self): + """Returns the match string + + Returns: + str: the value fo the match_string + """ return "FLOW" def __str__(self) -> str: @@ -75,32 +138,81 @@ def __str__(self) -> str: class IONode(Flow): - def __init__(self, id: str, iotype=None): + """ + The IONode is used to identify ports (inputs, outputs and control) + are represented using this fig node + """ + + def __init__(self, id: str, iotype: Optional[IOType] = None) -> None: + """Creates a new IONode + + Args: + id (str): ID of the IONode + iotype (Optional[IOType], optional): IOType of the node. Defaults to None. + """ super(IONode, self).__init__(id) - self._type = iotype + if iotype is None: + self._type = IOType.FLOW_INPUT + else: + self._type = iotype @property def type(self) -> IOType: + """Returns the IOType of the node + + Raises: + ValueError: If no IOType is not assigned + + Returns: + IOType: Type of IO for the node + """ + if self._type is None: + raise ValueError("Type not set for IO: {}".format(self.ID)) return self._type @type.setter def type(self, iotype: IOType) -> None: + """Sets the IOType of the node + + Args: + iotype (IOType): IOType we want to set + """ self._type = iotype def __str__(self) -> str: return "IO - Name: {0.ID}, Type : {0.type}".format(self) @property - def match_string(self): + def match_string(self) -> str: + """Returns the match string + + Returns: + str: the value fo the match_string + """ return "IO" class Storage(Flow): + """ + The Storage Node is the node that we use for represnting storage units + where flow stops. + """ + def __init__(self, id: str) -> None: + """Creates a new Storage element + + Args: + id (str): ID of the fignode + """ super(Storage, self).__init__(id) @property - def match_string(self): + def match_string(self) -> str: + """Returns the match string + + Returns: + str: the value fo the match_string + """ return "STORAGE" def __str__(self) -> str: @@ -108,11 +220,26 @@ def __str__(self) -> str: class Pump(Flow): + """The pump element is a node that represents + active flow movement within the fluid interaction + graph + """ + def __init__(self, id: str) -> None: + """Creates a new instance of a Pump elemeent + + Args: + id (str): ID of the pump + """ super(Pump, self).__init__(id) @property def match_string(self) -> str: + """Returns the match string + + Returns: + str: the value fo the match_string + """ return "PUMP" def __str__(self) -> str: @@ -120,11 +247,27 @@ def __str__(self) -> str: class Signal(FIGNode): + """ + The signal node represents the control signal + that flows through the device + """ + def __init__(self, id: str) -> None: + """Creates a new instance of the + signal node + + Args: + id (str): ID of the node + """ super(Signal, self).__init__(id) @property def match_string(self): + """Returns the match string + + Returns: + str: the value fo the match_string + """ return "SIGNAL" def __str__(self) -> str: diff --git a/lfr/fig/fluidinteractiongraph.py b/lfr/fig/fluidinteractiongraph.py index cf38f9a..1f70319 100644 --- a/lfr/fig/fluidinteractiongraph.py +++ b/lfr/fig/fluidinteractiongraph.py @@ -1,25 +1,19 @@ from __future__ import annotations import copy +import uuid +from typing import Dict, List, Tuple, Union import networkx as nx from lfr.compiler.distribute.statetable import StateTable -from typing import List, Dict, Tuple, Union -from lfr.fig.fignode import ( - FIGNode, - IONode, - IOType, - ValueNode, -) - from lfr.fig.annotation import ( ANDAnnotation, DistributeAnnotation, NOTAnnotation, ORAnnotation, ) - +from lfr.fig.fignode import FIGNode, IONode, IOType, ValueNode from lfr.fig.interaction import ( FluidFluidInteraction, FluidIntegerInteraction, @@ -29,11 +23,23 @@ InteractionType, ) -import uuid - class FluidInteractionGraph(nx.DiGraph): + """Fluid Interaction Graph + + This is the main data structure for the Fluid Interaction Graph. It is a directed graph + where the nodes are the fluid objects and the interactions between the fluid objects. + + It additionally stores the annotations and the state tables. + """ + def __init__(self, data=None, val=None, **attr) -> None: + """Constructor for the FluidInteractionGraph + + Args: + data (dict, optional): Networkx data dict. Defaults to None. + val (dict, optional): Networkx val dict. Defaults to None. + """ super(FluidInteractionGraph, self).__init__() self._fignodes: Dict[str, FIGNode] = {} # self._fluid_interactions = dict() @@ -45,33 +51,73 @@ def __init__(self, data=None, val=None, **attr) -> None: # Use this to store all the control to flow logic self._state_tables: List[StateTable] = [] - def add_state_table(self, state_table) -> None: + def add_state_table(self, state_table: StateTable) -> None: + """Adds a state table to the FluidInteractionGraph + + Args: + state_table (StateTable): State table to add + """ self._state_tables.append(state_table) @property def annotations(self) -> List[DistributeAnnotation]: + """Returns the list of annotations + + Returns: + List[DistributeAnnotation]: List of annotations + """ return self._annotations def get_annotation_by_id(self, id: str) -> DistributeAnnotation: + """Returns the annotation with the given ID + + Args: + id (str): ID of the annotation + + Raises: + KeyError: If the annotation is not found + + Returns: + DistributeAnnotation: Annotation with the given ID + """ for annotation in self._annotations: if annotation.id == id: return annotation - raise Exception("Cannot find the annotation with ID: {0}".format(id)) + raise KeyError(f"Cannot find the annotation with ID: {id}") def add_fignode(self, node: FIGNode) -> None: + """Adds a FIGNode to the FluidInteractionGraph + + Args: + node (FIGNode): FIGNode to add + """ self._fignodes[node.ID] = node self._annotations_reverse_map[node] = [] self.add_node(node.ID) def get_fignode(self, id: str) -> FIGNode: - if id in self._fignodes.keys(): + """Returns the FIGNode with the given ID + + Args: + id (str): ID of the FIGNode + + Raises: + Exception: If the FIGNode is not found + + Returns: + FIGNode: FIGNode with the given ID + """ + if id in self._fignodes: return self._fignodes[id] else: - raise Exception( - "Cannot find the node '{}' in the FluidInteractionGraph".format(id) - ) + raise Exception(f"Cannot find the node '{id}' in the FluidInteractionGraph") def load_fignodes(self, fig_nodes: List[FIGNode]) -> None: + """Loads the FIGNodes into the FluidInteractionGraph + + Args: + fig_nodes (List[FIGNode]): List of FIGNodes to load + """ for node in fig_nodes: self._fignodes[node.ID] = node @@ -79,6 +125,11 @@ def load_fignodes(self, fig_nodes: List[FIGNode]) -> None: self._annotations_reverse_map[node] = [] def load_annotations(self, annotations: List[DistributeAnnotation]) -> None: + """Loads the annotations into the FluidInteractionGraph + + Args: + annotations (List[DistributeAnnotation]): List of annotations to load + """ self._annotations.extend(annotations) for annotation in annotations: for item in annotation.get_items(): @@ -89,12 +140,31 @@ def load_annotations(self, annotations: List[DistributeAnnotation]) -> None: self.__add_to_reverse_map(item[1], annotation) def contains_fignode(self, fluid_object: FIGNode) -> bool: - return fluid_object.ID in self._fignodes.keys() + """Returns true if the FIGNode is present in the FluidInteractionGraph + + Args: + fluid_object (FIGNode): FIGNode to check + + Returns: + bool: True if the FIGNode is present in the FluidInteractionGraph + """ + return fluid_object.ID in self._fignodes def switch_fignode(self, old_fignode: FIGNode, new_fignode: FIGNode) -> None: + """Switch the old fignode with the new fignode + + Args: + old_fignode (FIGNode): the old fignode + new_fignode (FIGNode): the new fignode + """ self._fignodes[old_fignode.ID] = new_fignode def rename_nodes(self, rename_map: Dict[str, str]) -> None: + """Rename the nodes in the FIG + + Args: + rename_map (Dict[str, str]): a map from the old node ID to the new node ID + """ for node in self.nodes: fig_node = self._fignodes[node] fig_node.rename(rename_map[node]) @@ -108,6 +178,12 @@ def rename_nodes(self, rename_map: Dict[str, str]) -> None: def rename_annotations( self, fig_node_rename_map: Dict[str, str], annotation_rename_map: Dict[str, str] ) -> None: + """Rename the annotations in the FIG + + Args: + fig_node_rename_map (Dict[str, str]): a map from the old fignode ID to the new + annotation_rename_map (Dict[str, str]): a map from the old annotation ID to the + """ for annotation in self._annotations: annotation.rename(annotation_rename_map[annotation.id]) @@ -118,35 +194,37 @@ def rename_annotations( Union[Tuple[FIGNode, FIGNode], DistributeAnnotation] ] = [] - old_fignode_item_ID_list = [ + old_fignode_item_id_list = [ (item[0].ID, item[1].ID) for item in annotation.get_items() - if type(item) is tuple + if isinstance(item, tuple()) ] # Now switch the items to the new fignodes for ( old_fignode_item_id_1, old_fignode_item_id_2, - ) in old_fignode_item_ID_list: + ) in old_fignode_item_id_list: new_fignode_item_id_1 = fig_node_rename_map[old_fignode_item_id_1] new_fignode_item_id_2 = fig_node_rename_map[old_fignode_item_id_2] - item_to_add = ( + tuple_to_add: Tuple[FIGNode, FIGNode] = ( fig_copy.get_fignode(new_fignode_item_id_1), fig_copy.get_fignode(new_fignode_item_id_2), ) - new_items_list.append(item_to_add) + new_items_list.append(tuple_to_add) - old_annotation_item_ID_list = [ + old_annotation_item_id_list = [ item.id for item in annotation.get_items() if isinstance(item, DistributeAnnotation) ] - for old_annotation_item_id in old_annotation_item_ID_list: + for old_annotation_item_id in old_annotation_item_id_list: new_annotation_item_id = annotation_rename_map[old_annotation_item_id] - item_to_add = fig_copy.get_annotation_by_id(new_annotation_item_id) - new_items_list.append(item_to_add) + annotation_to_add: DistributeAnnotation = fig_copy.get_annotation_by_id( + new_annotation_item_id + ) + new_items_list.append(annotation_to_add) # now replace the annotation items with the new ones annotation.clear_items() @@ -154,12 +232,19 @@ def rename_annotations( annotation.add_annotated_item(item) def add_interaction(self, interaction: Interaction): - if interaction.ID not in self._fignodes.keys(): + """Add an interaction to the FIG + + Args: + interaction (Interaction): the interaction to add + + Raises: + Exception: If the interaction is already present in the FIG + Exception: If the interaction is of an invalid type + """ + if interaction.ID not in self._fignodes: self.add_fignode(interaction) else: - raise Exception( - "Interaction already present in the FIG: {0}".format(interaction.ID) - ) + raise Exception(f"Interaction already present in the FIG: {interaction.ID}") if isinstance(interaction, FluidFluidInteraction): self.__add_fluid_fluid_interaction(interaction) @@ -177,22 +262,32 @@ def add_interaction(self, interaction: Interaction): raise Exception("Invalid Interaction Type found here") def connect_fignodes(self, source: FIGNode, target: FIGNode): - if source.ID not in self._fignodes.keys(): + """Connect two fignodes in the FIG + + Args: + source (FIGNode): source fignode + target (FIGNode): target fignode + + Raises: + Exception: if the source fignode is not present in the FIG + """ + if source.ID not in self._fignodes: raise Exception( - "Unable to add interaction because of missing flow: {0}".format( - source.ID - ) + f"Unable to add interaction because of missing flow: {source.ID}" ) - if target.ID not in self._fignodes.keys(): + if target.ID not in self._fignodes: raise Exception( - "Unable to add interaction because of missing flow: {0}".format( - target.ID - ) + f"Unable to add interaction because of missing flow: {target.ID}" ) self.add_edge(source.ID, target.ID) def get_interactions(self) -> List[Interaction]: + """Get all the interactions in the FIG + + Returns: + List[Interaction]: a list of all the interactions in the FIG + """ ret = [] for item in self._fignodes.values(): if isinstance(item, Interaction): @@ -202,6 +297,11 @@ def get_interactions(self) -> List[Interaction]: @property def io(self) -> List[IONode]: + """Get all the IONodes in the FIG + + Returns: + List[IONode]: a list of all the IONodes in the FIG + """ ret = [] for key in self._fignodes: node = self._fignodes[key] @@ -211,15 +311,23 @@ def io(self) -> List[IONode]: return ret def get_fig_annotations(self, fig_node: FIGNode) -> List[DistributeAnnotation]: + """Get the annotations for a given FIGNode + + Args: + fig_node (FIGNode): the FIGNode to get the annotations for + + Returns: + List[DistributeAnnotation]: a list of the annotations for the given FIGNode + """ return self._annotations_reverse_map[fig_node] def add_and_annotation( self, fignode_tuples: List[Tuple[FIGNode, FIGNode]] ) -> ANDAnnotation: annotation_name = "DIST_AND_" + str(uuid.uuid4()) - print("Adding DIST-AND annotation '{}' for fig nodes:".format(annotation_name)) + print(f"Adding DIST-AND annotation '{annotation_name}' for fig nodes:") for item in fignode_tuples: - print("{}->{}".format(item[0], item[1])) + print(f"{item[0]}->{item[1]}") annotation = ANDAnnotation(annotation_name) self._annotations.append(annotation) self._annotations_reverse_map[annotation] = [] @@ -233,13 +341,22 @@ def add_or_annotation( self, constrained_items: List[Union[Tuple[FIGNode, FIGNode], DistributeAnnotation]], ) -> ORAnnotation: + """Add a new OR annotation to the FIG + + Args: + constrained_items (List[Union[Tuple[FIGNode, FIGNode], DistributeAnnotation]]): a list + of tuples of FIGNodes or DistributeAnnotations + + Returns: + ORAnnotation: the new ORAnnotation + """ annotation_name = "DIST_OR_" + str(uuid.uuid4()) - print("Adding DIST-OR annotation '{}' for fig nodes:".format(annotation_name)) + print(f"Adding DIST-OR annotation '{annotation_name}' for fig nodes:") for item in constrained_items: if isinstance(item, DistributeAnnotation): - print("{} (Annotation)".format(item.id)) + print(f"{item.id} (Annotation)") else: - print("{}->{}".format(item[0], item[1])) + print(f"{item[0]}->{item[1]}") annotation = ORAnnotation(annotation_name) self._annotations.append(annotation) @@ -257,9 +374,17 @@ def add_or_annotation( def add_not_annotation( self, fignode_tuple: Tuple[FIGNode, FIGNode] ) -> NOTAnnotation: + """Add a new NOT annotation to the FIG + + Args: + fignode_tuple (Tuple[FIGNode, FIGNode]): a tuple of FIGNodes + + Returns: + NOTAnnotation: the new NOTAnnotation + """ annotation_name = "DIST_NOT_" + str(uuid.uuid4()) - print("Adding DIST-AND annotation '{}' for fig nodes:".format(annotation_name)) - print("{}->{}".format(fignode_tuple[0], fignode_tuple[1])) + print(f"Adding DIST-AND annotation '{annotation_name}' for fig nodes:") + print(f"{fignode_tuple[0]}->{fignode_tuple[1]}") annotation = NOTAnnotation(annotation_name) self._annotations.append(annotation) @@ -270,10 +395,18 @@ def add_not_annotation( return annotation def add_fig(self, fig_to_add: FluidInteractionGraph) -> None: + """Add a FluidInteractionGraph to this FIG + + Args: + fig_to_add (FluidInteractionGraph): the FIG to add + + Raises: + Exception: if any of the nodes in the FIG to add are already present in this FIG + """ # Check if any of the incoming fig nodes are already present here for node_id in fig_to_add.nodes: - if node_id in self._fignodes.keys(): - raise Exception("Node '{}' already present in the FIG".format(node_id)) + if node_id in self._fignodes: + raise Exception(f"Node '{node_id}' already present in the FIG") self.add_fignode(fig_to_add.get_fignode(node_id)) for edge in fig_to_add.edges: @@ -283,6 +416,11 @@ def add_fig(self, fig_to_add: FluidInteractionGraph) -> None: self.load_annotations(fig_to_add.annotations) def get_input_fignodes(self) -> List[IONode]: + """Get all the input IONodes in the FIG + + Returns: + List[IONode]: a list of all the input IONodes in the FIG + """ ret = [] for fignode in self._fignodes.values(): if isinstance(fignode, IONode): @@ -310,7 +448,7 @@ def __deepcopy__(self, memo=None): fignodes_copy_list = [] # Map old_fignode <-> new_fignode - fignodes_copy_map: Dict[FIGNode, FIGNode] = dict() + fignodes_copy_map: Dict[FIGNode, FIGNode] = {} for fignode in self._fignodes.values(): fignode_copy = copy.copy(fignode) @@ -327,9 +465,7 @@ def __deepcopy__(self, memo=None): figannotations_copy_list = [] # Map old_annotatin <-> new_annotation - figannotations_copy_map: Dict[ - DistributeAnnotation, DistributeAnnotation - ] = dict() + figannotations_copy_map: Dict[DistributeAnnotation, DistributeAnnotation] = {} # Copy the annotations into the new fig copy for current_annotation in self._annotations: @@ -343,14 +479,14 @@ def __deepcopy__(self, memo=None): copy_annotation = figannotations_copy_map[current_annotation] for annotation_item in current_annotation.get_items(): if isinstance(annotation_item, DistributeAnnotation): - item_to_add = figannotations_copy_map[annotation_item] - copy_annotation.add_annotated_item(item_to_add) + annotation_to_add = figannotations_copy_map[annotation_item] + copy_annotation.add_annotated_item(annotation_to_add) else: - item_to_add = ( + tuple_to_add = ( fignodes_copy_map[annotation_item[0]], fignodes_copy_map[annotation_item[1]], ) - copy_annotation.add_annotated_item(item_to_add) + copy_annotation.add_annotated_item(tuple_to_add) fig_copy.load_annotations(figannotations_copy_list) return fig_copy @@ -362,40 +498,60 @@ def __add_to_reverse_map( item: Union[FIGNode, DistributeAnnotation], annotation: DistributeAnnotation, ) -> None: + """Adds to reverse map + + Args: + item (Union[FIGNode, DistributeAnnotation]): Add to reverse map + annotation (DistributeAnnotation): the annotation to add + + Raises: + Exception: if the annotation is already present in the reverse map + """ if self._annotations_reverse_map is None: self._annotations_reverse_map = dict() if isinstance(item, DistributeAnnotation): self.__add_to_reverse_map(item, annotation) else: - if item in self._annotations_reverse_map.keys(): + if item in self._annotations_reverse_map: annotation_list = self._annotations_reverse_map[item] if annotation not in annotation_list: annotation_list.append(annotation) else: - if annotation in self._annotations_reverse_map[item]: raise Exception("Annotation already present in the reverse map !") self._annotations_reverse_map[item] = [annotation] def __get_val_node_id(self) -> str: + """Get a unique ID for a value node + + Returns: + str: a unique ID for a value node + """ self._gen_id += 1 - return "val_{0}".format(self._gen_id) + return f"val_{self._gen_id}" def __add_fluid_fluid_interaction(self, interaction: FluidFluidInteraction) -> None: + """Adds a fluid-fluid interaction to the FIG + + Args: + interaction (FluidFluidInteraction): the interaction to add + + Raises: + Exception: if the interaction is not a fluid-fluid interaction + Exception: if the interaction is not a valid interaction + """ # Check if flow exists - if interaction.fluids[0].ID not in self._fignodes.keys(): + if interaction.fluids[0].ID not in self._fignodes: raise Exception( - "Unable to add interaction because of missing flow: {0}".format( - interaction.fluids[0].ID - ) + "Unable to add interaction because of missing flow:" + f" {interaction.fluids[0].ID}" ) - if interaction.fluids[1].ID not in self._fignodes.keys(): + if interaction.fluids[1].ID not in self._fignodes: raise Exception( - "Unable to add interaction because of missing flow: {0}".format( - interaction.fluids[1].ID - ) + "Unable to add interaction because of missing flow:" + f" {interaction.fluids[1].ID}" ) self.add_node(interaction.ID) @@ -414,11 +570,18 @@ def __add_fluid_fluid_interaction(self, interaction: FluidFluidInteraction) -> N def __add_single_fluid_interaction( self, interaction: FluidProcessInteraction ) -> None: - if interaction.fluid.ID not in self._fignodes.keys(): + """adds a single fluid interaction to the FIG + + Args: + interaction (FluidProcessInteraction): the interaction to add + + Raises: + Exception: if the interaction is not a fluid-process interaction + """ + if interaction.fluid.ID not in self._fignodes: raise Exception( - "Unable to add interaction because of missing flow: {0}".format( - interaction.fluid.ID - ) + "Unable to add interaction because of missing flow:" + " {interaction.fluid.ID}" ) self.add_node(interaction.ID) @@ -429,11 +592,18 @@ def __add_single_fluid_interaction( def __add_fluid_number_interaction( self, interaction: FluidNumberInteraction ) -> None: - if interaction.fluid.ID not in self._fignodes.keys(): + """adds a fluid-number interaction to the FIG + + Args: + interaction (FluidNumberInteraction): the interaction to add + + Raises: + Exception: if the interaction is not a fluid-number interaction + """ + if interaction.fluid.ID not in self._fignodes: raise Exception( - "Unable to add interaction because of missing flow: {0}".format( - interaction.fluid.ID - ) + "Unable to add interaction because of missing flow:" + f" {interaction.fluid.ID}" ) # Create new Value node @@ -447,11 +617,18 @@ def __add_fluid_number_interaction( def __add_fluid_integer_interaction( self, interaction: FluidIntegerInteraction ) -> None: - if interaction.fluid.ID not in self._fignodes.keys(): + """adds a fluid-integer interaction to the FIG + + Args: + interaction (FluidIntegerInteraction): the interaction to add + + Raises: + Exception: if the interaction is not a fluid-integer interaction + """ + if interaction.fluid.ID not in self._fignodes: raise Exception( - "Unable to add interaction because of missing flow: {0}".format( - interaction.fluid.ID - ) + "Unable to add interaction because of missing flow:" + f" {interaction.fluid.ID}" ) # Create new Value node diff --git a/lfr/fig/interaction.py b/lfr/fig/interaction.py index 5abd74f..6497140 100644 --- a/lfr/fig/interaction.py +++ b/lfr/fig/interaction.py @@ -14,7 +14,6 @@ class InteractionType(Enum): class Interaction(Flow): - INTERACTION_ID = 0 def __init__(self, id: str, interaction_type: InteractionType) -> None: @@ -38,10 +37,31 @@ def type(self) -> InteractionType: @staticmethod def get_id( - fluid1: FIGNode = None, fluid2: FIGNode = None, operator_string: str = "" + fluid1: FIGNode, fluid2: Optional[FIGNode] = None, operator_string: str = "" ) -> str: + """Generates a unique ID for the interaction + + The user needs to provide atleast one fignode and the operator string to generate the ID. + + Args: + fluid1 (FIGNode): First fignode + fluid2 (Optional[FIGNode]): Second FIGNode + operator_string (str): Operator String + + Raises: + ValueError: If there is no fignode provided + ValueError: If there is no operator string provided + + Returns: + str: unique ID for the interaction + """ + id = None + # If no operator string is given then we cannot proceed + if operator_string is None or operator_string == "": + raise ValueError("Operator string cannot be None") + if fluid1 is None: raise ValueError("id of fluid1 is found to be None") @@ -95,7 +115,7 @@ def __init__( fluid1: Flow, fluid2: Flow, interaction_type: InteractionType, - interaction_data: str = None, + interaction_data: Optional[str] = None, ) -> None: """Creates an interaction between two fluids diff --git a/lfr/fig/simplification.py b/lfr/fig/simplification.py new file mode 100644 index 0000000..95e1d0b --- /dev/null +++ b/lfr/fig/simplification.py @@ -0,0 +1,135 @@ +from typing import List + +import networkx as nx + +from lfr.fig.fignode import Flow +from lfr.fig.fluidinteractiongraph import FluidInteractionGraph + +"""FIG Simplification + +This class consists of all the methods that can be used to simplify/reduce +the fluid interaction graph. +""" + + +def remove_passthrough_nodes(fig: FluidInteractionGraph) -> None: + """Remove all the passthrough nodes from the fluid interaction graph. + + Args: + fig: The fluid interaction graph to remove the passthrough nodes from. + """ + + # Step 1. Do a shallow copy of the graph + # Step 2. Remove all the fignodes that are not Flow + # Step 3. Now get the all the disconnected pieces of the graph + # Step 4. Create a Construction node for each of the disconnected pieces + # Return all the constructions nodes + + # Step 1. Do a shallow copy of the graph + fig_original = fig + fig_copy = fig.copy( + as_view=False + ) # Note this does not copy anything besides the nx.DiGraph at the moment + + # Step 2. Remove all the fignodes that are not Flow + remove_list = [] + for node_id in fig_copy.nodes: + node = fig_original.get_fignode(node_id) + if node.match_string != "FLOW": + remove_list.append(node_id) + + for node_id in remove_list: + fig_copy.remove_node(node_id) + + i = 0 + # Step 3. Now get the all the disconnected pieces of the graph + for component in nx.connected_components(fig_copy.to_undirected()): + print("Flow candidate:", component) + sub = fig_original.subgraph(component) + # TODO - Decide what the mapping type should be. for now assume that we just a + # single passthrough type scenario where we don't have to do much work + is_passthrough = __check_if_passthrough(sub) + if is_passthrough: + print("Passthrough found:", component) + # Do the required remove candidate + # Find the input and output nodes + input_fignode = find_input_node(sub) + output_fignode = find_output_node(sub) + + # Find which nodes are connected to the input and output nodes + # Find the incoming neighbors of the input node + incoming_neighbors = [ + edge[0] for edge in list(fig_original.in_edges(input_fignode)) + ] + # Find the outgoing neighbors of the output node + outouting_neighbors = [ + edge[1] for edge in list(fig_original.out_edges(output_fignode)) + ] + + # Delete all the nodes in the component + for fig_node in component: + fig_original.remove_node(fig_node) + + # If |incoming_neighbors| == 1 and |outouting_neighbors| == 1 : delete the + # whole component and connect the input and the output else create a single + # flow node and make the connections + if len(incoming_neighbors) == 1 and len(outouting_neighbors) == 1: + print("Removing the component:", component) + # Connect the input and output nodes + fig_original.add_edge(incoming_neighbors[0], outouting_neighbors[0]) + else: + if input_fignode == output_fignode: + print( + "Since its a single flow node, we are skipping the component:", + component, + ) + continue + # Create a new FLOW node + flow_node = Flow(f"FLOW_component_replacement_{i}") + i += 0 + print("Replacing the component with:", flow_node) + # Add the flow node to the graph + fig_original.add_fignode(flow_node) + + # Connect the input and output nodes + for incoming_neighbor_id in incoming_neighbors: + incoming_neighbor = fig_original.get_fignode(incoming_neighbor_id) + fig_original.connect_fignodes(incoming_neighbor, flow_node) + + for outouting_neighbor_id in outouting_neighbors: + outgoing_neighbor = fig_original.get_fignode(outouting_neighbor_id) + fig_original.connect_fignodes(flow_node, outgoing_neighbor) + + +def __check_if_passthrough(sub: nx.DiGraph) -> bool: + # Return true if its a single chain of flow channels + in_count = 0 + out_count = 0 + for node in list(sub.nodes): + inedges = list(sub.in_edges(node)) + outedges = list(sub.out_edges(node)) + if len(inedges) == 0: + in_count += 1 + if len(outedges) == 0: + out_count += 1 + + if in_count == 1 and out_count == 1: + return True + else: + return False + + +def find_input_node(sub: nx.DiGraph) -> str: + for node in list(sub.nodes): + inedges = list(sub.in_edges(node)) + if len(inedges) == 0: + return node + raise Exception("No input node found") + + +def find_output_node(sub: nx.DiGraph) -> str: + for node in list(sub.nodes): + outedges = list(sub.out_edges(node)) + if len(outedges) == 0: + return node + raise Exception("No input node found") diff --git a/lfr/graphmatch/figmappingmatcher.py b/lfr/graphmatch/figmappingmatcher.py index 9ea7bdb..ed4fcee 100644 --- a/lfr/graphmatch/figmappingmatcher.py +++ b/lfr/graphmatch/figmappingmatcher.py @@ -1,8 +1,10 @@ from typing import Dict + from networkx.algorithms.isomorphism import DiGraphMatcher from networkx.classes import digraph -from lfr.graphmatch.nodefilter import NodeFilter + from lfr.fig.fluidinteractiongraph import FluidInteractionGraph +from lfr.graphmatch.nodefilter import NodeFilter class FIGMappingMatcher(DiGraphMatcher): diff --git a/lfr/graphmatch/interface.py b/lfr/graphmatch/interface.py index b58036b..9e1b616 100644 --- a/lfr/graphmatch/interface.py +++ b/lfr/graphmatch/interface.py @@ -1,10 +1,12 @@ +from typing import Dict, FrozenSet, List, Optional, Tuple + from lfr.fig.annotation import DistributeAnnotation -from lfr.graphmatch.figmappingmatcher import FIGMappingMatcher -from typing import Any, Dict, FrozenSet, List, Tuple -from lfr.netlistgenerator.mappinglibrary import MappingLibrary from lfr.fig.fluidinteractiongraph import FluidInteractionGraph +from lfr.graphmatch.figmappingmatcher import FIGMappingMatcher from lfr.graphmatch.matchpattern import MatchPattern from lfr.graphmatch.nodefilter import NodeFilter +from lfr.netlistgenerator import LibraryPrimitivesEntry +from lfr.netlistgenerator.mappinglibrary import MappingLibrary def bijective_match_node_constraints( @@ -12,7 +14,7 @@ def bijective_match_node_constraints( semantic_information: Dict[str, NodeFilter], subgraph: Dict[str, str], ) -> bool: - # TODO - Check if the constraints match for the subgraph + # Check if the constraints match for the subgraph # STEP 1 - generate new unique names for each node to simplify the matching # algorihtm @@ -153,8 +155,10 @@ def bijective_match_node_constraints( return False # Step 4.1.2 - Check if each of the values in the fig_node_dict is in the # match_node_dict - fig_rels_list = fig_node_dict[ids_set].sort() - match_rels_list = match_node_dict[ids_set].sort() + fig_rels_list = fig_node_dict[ids_set] + fig_rels_list.sort() + match_rels_list = match_node_dict[ids_set] + match_rels_list.sort() if fig_rels_list != match_rels_list: return False # Step 4.2 - Check if the relationships between the fig and the match networks are @@ -166,8 +170,10 @@ def bijective_match_node_constraints( return False # Step 4.2.2 - Check if each of the values in the match_node_dict is in the # fig_node_dict - fig_rels_list = fig_node_dict[ids_set].sort() - match_rels_list = match_node_dict[ids_set].sort() + fig_rels_list = fig_node_dict[ids_set] + fig_rels_list.sort() + match_rels_list = match_node_dict[ids_set] + match_rels_list.sort() if fig_rels_list != match_rels_list: return False @@ -177,22 +183,30 @@ def bijective_match_node_constraints( def get_fig_matches( fig: FluidInteractionGraph, library: MappingLibrary -) -> List[Tuple[str, Any]]: - patterns: Dict[ - str, MatchPattern - ] = dict() # Store the mint and the match pattern object here +) -> List[LibraryPrimitivesEntry]: + """Get the matches for the given FluidInteractionGraph and MappingLibrary. + + + Args: + fig (FluidInteractionGraph): FIG to match. + library (MappingLibrary): Library to match against + Returns: + List[LibraryPrimitivesEntry]: This returns a list of tuples of the + matches ordered this way: (primitive_uid, primitive_mint, match_node_dict). The + keys in the match_node_dict is from the FIG and the values are from the match + pattern. + """ ret = [] # TODO - Retrun the networkx subgraph views of the of the FIG # Step 1 - Generate the match candidates by running the subgraph isomerism for all # the items stored in the library - for (minty_uid, match_pattern_string) in library.get_match_patterns(): + for minty_uid, mint, match_pattern_string in library.get_match_patterns(): if match_pattern_string == "" or match_pattern_string is None: print("Warning ! - Missing match string for mint- {}".format(minty_uid)) continue pattern = MatchPattern(match_pattern_string) - patterns[minty_uid] = pattern structural_template = pattern.get_structural_template() semantic_information = pattern.get_semantic_template() GM = FIGMappingMatcher(fig, structural_template, semantic_information) @@ -241,7 +255,7 @@ def get_fig_matches( print(subgraph) # MATCH - ret.append((minty_uid, subgraph)) + ret.append((minty_uid, mint, subgraph)) # SKIP continue @@ -263,9 +277,39 @@ def get_fig_matches( print("Found Match: {}".format(minty_uid)) print(subgraph) - ret.append((minty_uid, subgraph)) + ret.append((minty_uid, mint, subgraph)) else: # NO-MATCH, SKIP continue return ret + + +def find_structural_matches( + match, + privitives: List[LibraryPrimitivesEntry], +) -> List[LibraryPrimitivesEntry]: + """Finds all the primitives with the matching structural template. + + Args: + privitives (List[LibraryPrimitivesEntry]): List of primitives to match. + + Returns: + List[LibraryPrimitivesEntry]: List of primitives that match + """ + + # Go through each of the primitives and find the ones that match the structure + # template + raise NotImplementedError() + return privitives + + +def generate_single_match( + fig_subgraph, library_entry +) -> Optional[Tuple[str, Dict[str, str]]]: + # TODO - using fig subgraph view test to see if the subgraph is a structural match + # to technology entry from the mapping library, pass back the match tuple if it is + # if it isn't then figure out how to do this separately. Also don't enable node + # filters for this step. Enabling them will cause the match to fail. + raise NotImplementedError() + return ("test", {"test": "test"}) diff --git a/lfr/graphmatch/matchpattern.py b/lfr/graphmatch/matchpattern.py index fafe6a4..5516e42 100644 --- a/lfr/graphmatch/matchpattern.py +++ b/lfr/graphmatch/matchpattern.py @@ -1,18 +1,20 @@ from __future__ import annotations + +from typing import Dict + +from antlr4 import InputStream from antlr4.CommonTokenStream import CommonTokenStream from antlr4.tree.Tree import ParseTreeWalker -from lfr.graphmatch.nodefilter import NodeFilter from networkx.classes.digraph import DiGraph -from typing import Dict -from antlr4 import InputStream + from lfr.antlrgen.reggie.reggieLexer import reggieLexer from lfr.antlrgen.reggie.reggieParser import reggieParser from lfr.graphmatch.matchpatterngenerator import MatchPatternGenerator +from lfr.graphmatch.nodefilter import NodeFilter class MatchPattern: def __init__(self, pattern_string: str = "") -> None: - if pattern_string == "" or pattern_string is None: raise Exception("Empty Pattern found") @@ -20,7 +22,7 @@ def __init__(self, pattern_string: str = "") -> None: self._structural_template = None # Dictionary that stores the nodefilter object and the node id - self._semantic_template = dict() + self._semantic_template: Dict[str, NodeFilter] = {} self.__parse_pattern(pattern_string) @@ -56,4 +58,4 @@ def __parse_pattern(self, pattern) -> None: walker.walk(listener, tree) self._structural_template = listener.structural_template - self._semantic_template = listener.semantic_template \ No newline at end of file + self._semantic_template = listener.semantic_template diff --git a/lfr/graphmatch/matchpatterngenerator.py b/lfr/graphmatch/matchpatterngenerator.py index b40710b..4eb6383 100644 --- a/lfr/graphmatch/matchpatterngenerator.py +++ b/lfr/graphmatch/matchpatterngenerator.py @@ -1,16 +1,19 @@ -from lfr.graphmatch.nodefilter import NodeFilter -from lfr.antlrgen.reggie.reggieParser import reggieParser -from lfr.antlrgen.reggie.reggieListener import reggieListener +from typing import Dict, List + import networkx as nx +from lfr.antlrgen.reggie.reggieListener import reggieListener +from lfr.antlrgen.reggie.reggieParser import reggieParser +from lfr.graphmatch.nodefilter import NodeFilter + class MatchPatternGenerator(reggieListener): def __init__(self) -> None: super(MatchPatternGenerator, self).__init__() self.structural_template = nx.DiGraph() - self.semantic_template = dict() - self._vertices_stack = [] + self.semantic_template: Dict[str, NodeFilter] = {} + self._vertices_stack: List[str] = [] def enterVertex(self, ctx: reggieParser.VertexContext): vertex_id = ctx.structuralid().getText() diff --git a/lfr/graphmatch/nodefilter.py b/lfr/graphmatch/nodefilter.py index 98f123b..1b9daf7 100644 --- a/lfr/graphmatch/nodefilter.py +++ b/lfr/graphmatch/nodefilter.py @@ -1,4 +1,5 @@ from __future__ import annotations + from typing import List, Tuple diff --git a/lfr/lfrbaseListener.py b/lfr/lfrbaseListener.py index 60033b1..6f05a29 100644 --- a/lfr/lfrbaseListener.py +++ b/lfr/lfrbaseListener.py @@ -1,8 +1,10 @@ -from os import error import re from enum import Enum +from os import error from typing import List, Optional +from lfr.antlrgen.lfr.lfrXListener import lfrXListener +from lfr.antlrgen.lfr.lfrXParser import lfrXParser from lfr.compiler.distribute.BitVector import BitVector from lfr.compiler.language.concatenation import Concatenation from lfr.compiler.language.fluidexpression import FluidExpression @@ -13,8 +15,6 @@ from lfr.compiler.module import Module from lfr.compiler.moduleio import ModuleIO from lfr.fig.fignode import Flow, IONode, IOType, Pump, Signal, Storage -from lfr.antlrgen.lfr.lfrXListener import lfrXListener -from lfr.antlrgen.lfr.lfrXParser import lfrXParser class ListenerMode(Enum): @@ -36,7 +36,6 @@ class VariableTypes(Enum): class LFRBaseListener(lfrXListener): def __init__(self): - print("Initialized the lfrcompiler") self.modules = [] self.currentModule: Optional[Module] = None @@ -59,7 +58,7 @@ def __init__(self): # This might be the new expression stack self.stack = [] self.statestack = [] - self.binaryoperatorsstack = [[]] + self.binaryoperatorsstack: List[List[str]] = [[]] # TODO - Figure out how to make this more elegant self._lhs_store = None @@ -86,14 +85,17 @@ def exitModule(self, ctx: lfrXParser.ModuleContext): self.success = False self.vectors = {} self.expressionresults = None - self.listermode: ListenerMode = ListenerMode.NONE - self.lastlistenermode: ListenerMode = ListenerMode.NONE + self.listermode = ListenerMode.NONE + self.lastlistenermode = ListenerMode.NONE self.stack = [] self.statestack = [] self.binaryoperatorsstack = [[]] def enterIoblock(self, ctx: lfrXParser.IoblockContext): + if self.currentModule is None: + raise ValueError("currentModule set to None") + # If io block has an explicit declaration set the flag if self.currentModule is None: raise ValueError("currentModule set to None") @@ -115,11 +117,14 @@ def enterIoblock(self, ctx: lfrXParser.IoblockContext): self.vectors[name] = v self.typeMap[name] = VariableTypes.FLUID - m = ModuleIO(name) - m.vector_ref = v.get_range() + m = ModuleIO(name, IOType.FLOW_INPUT) + m.vector_ref = VectorRange.get_range_from_vector(v) self.currentModule.add_io(m) def exitExplicitIOBlock(self, ctx: lfrXParser.ExplicitIOBlockContext): + if self.currentModule is None: + raise ValueError("currentModule set to None") + # First check the type of the explicit io block decltype = ctx.start.text mode = None @@ -170,9 +175,12 @@ def exitExplicitIOBlock(self, ctx: lfrXParser.ExplicitIOBlockContext): for io in vec.get_items(): io.type = mode + # If IOType is None + if mode is None: + raise ValueError("IOType is None") # Create and add a ModuleIO reference m = ModuleIO(name, mode) - m.vector_ref = vec.get_range() + m.vector_ref = VectorRange.get_range_from_vector(vec) self.currentModule.add_io(m) else: @@ -184,6 +192,9 @@ def exitExplicitIOBlock(self, ctx: lfrXParser.ExplicitIOBlockContext): ) def enterFluiddeclstat(self, ctx: lfrXParser.FluiddeclstatContext): + if self.currentModule is None: + raise ValueError("currentModule set to None") + self.__updateMode(ListenerMode.VARIABLE_DECLARATION_MODE) for declvar in ctx.declvar(): name = declvar.ID().getText() @@ -197,6 +208,8 @@ def enterFluiddeclstat(self, ctx: lfrXParser.FluiddeclstatContext): v = self.__createVector(name, Flow, startindex, endindex) for item in v.get_items(): + if self.currentModule is None: + raise ValueError("currentModule set to None") self.currentModule.add_fluid(item) # Now that the declaration is done, we are going to save it @@ -207,6 +220,9 @@ def exitFluiddeclstat(self, ctx: lfrXParser.FluiddeclstatContext): self.__revertMode() def enterStoragestat(self, ctx: lfrXParser.StoragestatContext): + if self.currentModule is None: + raise ValueError("currentModule set to None") + self.__updateMode(ListenerMode.VARIABLE_DECLARATION_MODE) for declvar in ctx.declvar(): name = declvar.ID().getText() @@ -230,6 +246,9 @@ def exitStoragestat(self, ctx: lfrXParser.StoragestatContext): self.__revertMode() def enterPumpvarstat(self, ctx: lfrXParser.PumpvarstatContext): + if self.currentModule is None: + raise ValueError("currentModule set to None") + self.__updateMode(ListenerMode.VARIABLE_DECLARATION_MODE) for declvar in ctx.declvar(): name = declvar.ID().getText() @@ -253,6 +272,9 @@ def exitPumpvarstat(self, ctx: lfrXParser.PumpvarstatContext): self.__revertMode() def enterSignalvarstat(self, ctx: lfrXParser.SignalvarstatContext): + if self.currentModule is None: + raise ValueError("currentModule set to None") + self.__updateMode(ListenerMode.VARIABLE_DECLARATION_MODE) for declvar in ctx.declvar(): name = declvar.ID().getText() @@ -426,7 +448,6 @@ def exitBracketexpression(self, ctx: lfrXParser.BracketexpressionContext): self.__revertMode() # Perform the unary operation if present if ctx.unary_operator() is not None: - operator = ctx.unary_operator().getText() term = self.stack.pop() if self.currentModule is None: @@ -509,7 +530,6 @@ def __createVector( return v def __createLiteralVector(self, name: str, values: List) -> Vector: - objectype = None if isinstance(values, list): diff --git a/lfr/moduleinstanceListener.py b/lfr/moduleinstanceListener.py index 6b08741..0e43377 100644 --- a/lfr/moduleinstanceListener.py +++ b/lfr/moduleinstanceListener.py @@ -1,9 +1,9 @@ from os import error from typing import Dict, Optional +from lfr.antlrgen.lfr.lfrXParser import lfrXParser from lfr.compiler.lfrerror import ErrorType, LFRError from lfr.compiler.module import Module -from lfr.antlrgen.lfr.lfrXParser import lfrXParser from lfr.distBlockListener import DistBlockListener @@ -17,6 +17,9 @@ def __init__(self) -> None: def enterModuleinstantiationstat( self, ctx: lfrXParser.ModuleinstantiationstatContext ): + if self.currentModule is None: + raise ValueError("currentModule set to None") + # Check if the type exists in current compiler memory type_id = ctx.moduletype().getText() module_to_import = None @@ -39,6 +42,9 @@ def enterModuleinstantiationstat( def exitModuleinstantiationstat( self, ctx: lfrXParser.ModuleinstantiationstatContext ): + if self.currentModule is None: + raise ValueError("currentModule set to None") + # Create new instance of the import the type type_id = ctx.moduletype().getText() io_mapping = self._io_mapping diff --git a/lfr/netlistgenerator/__init__.py b/lfr/netlistgenerator/__init__.py index e69de29..79679e7 100644 --- a/lfr/netlistgenerator/__init__.py +++ b/lfr/netlistgenerator/__init__.py @@ -0,0 +1,8 @@ +"""Types generally used around there to pass along data + +TODO - Add detailed descriptions of all types +""" + +from typing import Dict, Tuple + +LibraryPrimitivesEntry = Tuple[str, str, Dict[str, str]] diff --git a/lfr/netlistgenerator/connectingoption.py b/lfr/netlistgenerator/connectingoption.py index 0cdca43..c612f60 100644 --- a/lfr/netlistgenerator/connectingoption.py +++ b/lfr/netlistgenerator/connectingoption.py @@ -5,7 +5,7 @@ class ConnectingOption: def __init__( self, component_name: Optional[str] = None, - component_port: List[Optional[str]] = None, + component_port: List[Optional[str]] = [], ) -> None: if component_port is None: component_port = [] diff --git a/lfr/netlistgenerator/connection_primitive.py b/lfr/netlistgenerator/connection_primitive.py new file mode 100644 index 0000000..6992c99 --- /dev/null +++ b/lfr/netlistgenerator/connection_primitive.py @@ -0,0 +1,37 @@ +import hashlib + +from lfr.netlistgenerator.primitive import Primitive, PrimitiveType + + +class ConnectionPrimitive(Primitive): + """ + Connection primitive class. + """ + + def __init__(self, mint: str = "") -> None: + """Initializes the connection primitive. + + Args: + mint (str, optional): MINT string to initialize the connection primitive. Defaults to "". + """ + super().__init__(mint) + self._type = PrimitiveType.CONNECTION + self._uid = hashlib.md5(f"{self._mint}".encode("utf-8")).hexdigest() + + @property + def mint(self) -> str: + """Returns the MINT string for the connection primitive. + + Returns: + str: MINT string for the connection primitive + """ + return self._mint + + @property + def uid(self) -> str: + """Returns the UID of the connection primitive. + + Returns: + str: UID of the connection primitive + """ + return self._uid diff --git a/lfr/netlistgenerator/constructiongraph/__init__.py b/lfr/netlistgenerator/constructiongraph/__init__.py new file mode 100644 index 0000000..5dfc931 --- /dev/null +++ b/lfr/netlistgenerator/constructiongraph/__init__.py @@ -0,0 +1,2 @@ +from .constructiongraph import ConstructionGraph +from .constructionnode import ConstructionNode diff --git a/lfr/netlistgenerator/constructiongraph/constructiongraph.py b/lfr/netlistgenerator/constructiongraph/constructiongraph.py new file mode 100644 index 0000000..647a8bb --- /dev/null +++ b/lfr/netlistgenerator/constructiongraph/constructiongraph.py @@ -0,0 +1,160 @@ +from __future__ import annotations + +import os +from typing import FrozenSet, List + +import networkx as nx + +from lfr import parameters +from lfr.fig.fluidinteractiongraph import FluidInteractionGraph +from lfr.netlistgenerator.constructiongraph.constructionnode import ConstructionNode + + +class ConstructionGraph(nx.DiGraph): + """ + This class is a sub-class of networkx.DiGraph. + It acts as a proxy datastructure for generating the device netlist. + """ + + def __init__(self, id: str, fig: FluidInteractionGraph) -> None: + """Initializes the construction graph + + Args: + id (str): ID of the construction graph + fig (FluidInteractionGraph): Fluid interaction graph which the construction + """ + super().__init__() + self._id = id + self._fig = fig + self._construction_nodes: List[ConstructionNode] = [] + + @property + def ID(self) -> str: + """Returns the ID of the construction graph + + Returns: + str: ID of the construction graph + """ + return self._id + + @property + def construction_nodes(self) -> List[ConstructionNode]: + return self._construction_nodes + + def add_construction_node(self, construction_node: ConstructionNode) -> None: + """Adds a construction node to the graph + + Args: + construction_node (ConstructionNode): Node to add the the construction graph + """ + + self._construction_nodes.append(construction_node) + self.add_node(construction_node.ID) + + def remove_construction_node(self, construction_node: ConstructionNode) -> None: + """Remove a construction node from the graph + + Args: + construction_node (ConstructionNode): Node to remove from the construction + graph + """ + # Remove the construction node from the graph + self.remove_node(construction_node.ID) + self._construction_nodes.remove(construction_node) + + def get_construction_node(self, id: str) -> ConstructionNode: + """Returns the construction node with the given id + + Args: + id (str): ID of the construction node to return + + Raises: + ValueError: If the construction node with the given id does not exist + + Returns: + ConstructionNode: Construction node with the given id + """ + for cn in self._construction_nodes: + if cn.ID == id: + return cn + else: + raise ValueError("No construction node with id: " + id) + + def connect_nodes(self, node_a: ConstructionNode, node_b: ConstructionNode) -> None: + """ + This method connects two nodes in the graph. + """ + self.add_edge(node_a.ID, node_b.ID) + + def is_fig_fully_covered(self) -> bool: + """ + This method checks if the FIG is fully covered by the construction graph + """ + # Check if all the fig nodes are covered by construction nodes fig_subgraph + # Create a set of all the fig node ids + # Go through each of the construction nodes and the corresponding fig subgraph + # nodes if the fig subgraph node is not in the list of fig node ids, then the + # graph is not fully covered + fig_node_set = set([node for node in self._fig.nodes]) + for cn in self._construction_nodes: + fig_subgraph = cn.fig_subgraph + for node in fig_subgraph.nodes: + if node not in fig_node_set: + return False + else: + return True + + def generate_variant(self, new_id: str) -> ConstructionGraph: + # Generate a variant of the construction graph + ret = ConstructionGraph(new_id, self._fig) + for cn in self._construction_nodes: + ret.add_construction_node(cn) + # Get the existing edges and add them to the new graph + for edge in self.edges: + ret.add_edge(edge[0], edge[1]) + return ret + + def __eq__(self, __o: object) -> bool: + if isinstance(__o, ConstructionGraph): + if self.ID == __o.ID: + return True + else: + return False + else: + return False + + def remove_node_for_exact_fig_cover(self, fig_node_cover: FrozenSet[str]) -> None: + """Removes the construction node which contains the exact fig node cover + provided. + + This method Removes the construction node which contains the exact fig node + cover provided. i.e. if the fig_node_cover is {'A', 'B'} and the construction + should have a fig node mapping of {'A', 'B'}. This covers the cases where the + mapping is an exact match and you need to substitute the entire construction + node. + + Use other methods when you need to account for partial cover matches. + + Args: + fig_node_cover (FrozenSet[str]): A frozen set of fig node IDs which + represents the exact fig node cover. + """ + + for cn in self._construction_nodes: + if frozenset(list(cn.fig_subgraph.nodes)) == fig_node_cover: + self.remove_node(cn.ID) + break + + def __str__(self): + ret = "Construction Graph: " + self.ID + return ret + + def print_graph(self, filename: str) -> None: + """Prints the graph to a file + + Args: + filename (str): Name of the file to print the graph to + """ + tt = os.path.join(parameters.OUTPUT_DIR, filename) + print("File Path:", tt) + nx.nx_agraph.to_agraph(self).write(tt) diff --git a/lfr/netlistgenerator/constructiongraph/constructionnode.py b/lfr/netlistgenerator/constructiongraph/constructionnode.py new file mode 100644 index 0000000..0ae37b2 --- /dev/null +++ b/lfr/netlistgenerator/constructiongraph/constructionnode.py @@ -0,0 +1,247 @@ +from __future__ import annotations + +from typing import List, Optional, Set + +import networkx as nx + +from lfr.netlistgenerator.connectingoption import ConnectingOption +from lfr.netlistgenerator.mappingoption import MappingOption +from lfr.netlistgenerator.primitive import Primitive +from lfr.postprocessor.constraints import Constraint + + +class ConstructionNode: + def __init__( + self, + node_id: str, + primitive: Optional[Primitive] = None, + subgraph_view: Optional[nx.DiGraph] = None, + ) -> None: + """Initializes the construction node + + Args: + node_id (str): ID of the node + primitive (Optional[Primitive], optional): Primitve to map. Defaults to + None. + subgraph_view (Optional[nx.DiGraph], optional): subgraph view of the FIG. + Defaults to None. + """ + self._id = node_id + self._explict_mapping_flag = False + self._fig_subgraph: Optional[nx.DiGraph] = subgraph_view + self._primitive: Optional[Primitive] = primitive + + # Connection options that we want to load here + self._input_options: List[ConnectingOption] = [] + self._output_options: List[ConnectingOption] = [] + self._loading_options: List[ConnectingOption] = [] + self._carrier_options: List[ConnectingOption] = [] + + # Mapping Constraints + # These will be all the imported constraints + self._constraints: List[Constraint] = [] + + # Load the connection options + if self.primitive is not None: + self._load_connection_options() + + @property + def primitive(self) -> Primitive: + """Returns the primitive that is associated with this construction node + + Returns: + Primitive: Primitive that is associated with this construction node + """ + if self._primitive is None: + raise ValueError(f"Primitive not set for construction node {self.ID}") + return self._primitive + + @primitive.setter + def primitive(self, primitive: Primitive) -> None: + """Sets the primitive for the construction node + + Args: + primitive (Primitive): Primitive that needs to be set + """ + self._primitive = primitive + self._load_connection_options() + + @property + def fig_subgraph(self) -> nx.DiGraph: + """Returns the FIG subgraph of the node + + Returns: + nx.Digraph: FIG subgraph of the node + """ + if self._fig_subgraph is None: + raise Exception(f"FIG subgraph not set for construction node {sel}") + return self._fig_subgraph + + @fig_subgraph.setter + def fig_subgraph(self, subgraph: nx.DiGraph) -> None: + """Sets the FIG subgraph view of the node + + Args: + subgraph (nx.DiGraph): Subgraph view of the node + """ + self._fig_subgraph = subgraph + + @property + def is_explictly_mapped(self) -> bool: + """Checks if the node is explicitly mapped or not + + Returns: + bool: True if the node is explicitly mapped, False otherwise + """ + return self._explict_mapping_flag + + @property + def constraints(self) -> List[Constraint]: + """Returns the constraints that are associated with this node + + Returns: + List[Constraint]: List of constraints associated with this node + """ + return self._constraints + + @constraints.setter + def constraints(self, vals: List[Constraint]) -> None: + """Sets the constraints for the node + + Args: + vals (List[Constraint]): List of constraints to set + """ + self._constraints = vals + + @property + def input_options(self) -> List[ConnectingOption]: + """Returns the input options of the construction node + + Returns: + List[ConnectingOption]: List of input options + """ + return self._input_options + + @property + def output_options(self) -> List[ConnectingOption]: + """Returns the output options of the construction node + + Returns: + List[ConnectingOption]: List of output options + """ + return self._output_options + + @property + def loading_options(self) -> List[ConnectingOption]: + """Returns the list of loading options for the mapption option candidate + + Returns: + List[ConnectingOption]: List of loading options + """ + return self._loading_options + + @property + def carrier_options(self) -> List[ConnectingOption]: + """Returns the list of carrier options set for the construction node + + Returns: + List[ConnectingOption]: List of carrier options + """ + return self._carrier_options + + @property + def ID(self) -> str: + """Returns the id of the construction node + + Returns: + str: ID of the construction node + """ + return self._id + + @property + def fig_cover(self) -> Set[str]: + """Returns the cover of the figure subgraph + + Returns: + Set[str]: Cover of the figure subgraph + """ + return set(self.fig_subgraph.nodes) + + def use_explicit_mapping(self, mapping: MappingOption) -> None: + """Uses the explicit mapping option passed as the parameter + + Args: + mapping (MappingOption): MappingOption that needs to set + here explicitly + """ + # Set the flag for explicit mapping + self._explict_mapping_flag = True + # Delete all the existing mapping options + self._primitive = mapping.primitive + # Now that we have overwritten all the netlist options here + # we basically cherry pick the one little bit that we want to attach here + self._load_connection_options() + + def _load_connection_options(self) -> None: + """Loads the corresponding different connecting options into + the construction node + """ + # Load the input options + if self.primitive is None: + raise ValueError(f"Primitive not set for construction node {self.ID}") + + self._input_options = self.primitive.export_inputs(self.fig_subgraph) + self._output_options = self.primitive.export_outputs(self.fig_subgraph) + + options = self.primitive.export_loadings(self.fig_subgraph) + if options is None: + options = [] + + self._loading_options = options + + options = self.primitive.export_carriers(self.fig_subgraph) + if options is None: + options = [] + + self._carrier_options = options + + def merge_construction_node(self, construction_node: ConstructionNode) -> None: + """Merges the construction node passed as an arugment into the this + construction node. + + This will let us handle the scenario where we might want to merge + construction nodes that have undercoverage and help support the future + combinatorial generation options + + + Args: + construction_node (ConstructionNode): [description] + """ + raise NotImplementedError( + "Implement this when we are trying to make combinatorial operations work" + ) + + def has_border_overlap(self, other_node: ConstructionNode) -> bool: + """Checks if the border of the current node overlaps with the border""" + + # Step 1 - Get the intersection of the two covers, If the intersection is empty + # then we do not have a border overlap + # Step 2 - If any of thos are in the border of the subgraph (i.e. the incoming + # or outgoing edges are 0) + # then we have a border overlap + + intersection_cover = self.fig_cover.intersection(other_node.fig_cover) + if len(intersection_cover) == 0: + return False + else: + # Check if any of the intersection cover is in the border of the subgraph + for node in intersection_cover: + if ( + self.fig_subgraph.in_degree(node) == 0 + or self.fig_subgraph.out_degree(node) == 0 + ): + return True + return False + + def __str__(self) -> str: + return f"Construction Node: {self.ID}" diff --git a/lfr/netlistgenerator/constructiongraph/edge_generation.py b/lfr/netlistgenerator/constructiongraph/edge_generation.py new file mode 100644 index 0000000..6ee1870 --- /dev/null +++ b/lfr/netlistgenerator/constructiongraph/edge_generation.py @@ -0,0 +1,285 @@ +from typing import List + +from lfr.fig.fluidinteractiongraph import FluidInteractionGraph +from lfr.netlistgenerator.constructiongraph.constructiongraph import ConstructionGraph +from lfr.netlistgenerator.constructiongraph.constructionnode import ConstructionNode + +# def _bridge_channel_networks(construction_graph: ConstructionGraph) -> None: +# # TODO - Bridge the channel networks +# # Find all the passthrough nodes in the fig +# # Make a copy of the fig and eliminate all the fig nodes that are either not +# # FLOW or are not covered by the construction nodes +# copy_fig = self._fig.copy(as_view=False) +# for cn in self._construction_nodes: +# for fig_node in cn.fig_subgraph.nodes: +# copy_fig.remove_node(fig_node.ID) +# # Delete all the non-flow nodes +# for node in copy_fig.nodes: +# if isinstance(node, Flow) is not True and isinstance(node, Signal) is not True: +# copy_fig.remove_node(node.ID) + +# # Get all the disconnected components in the fig +# components = list(nx.connected_components(copy_fig)) +# # Check if each of the components are pass through or not +# for component in components: +# is_passthrough = self.__check_if_passthrough(component) +# if is_passthrough is True: +# # Generate an edge bewtween the passthrough nodes +# self.__generate_edge_between_passthrough_nodes(component) + + +# def generate_construction_edges(self) -> None: +# """ +# This method generates the connections between the nodes in the graph. +# """ +# # Step 1 - Generate a map where the key is a fig node and the value is a list of +# # construction nodes that have a fig node in their fig_subgraph +# fig_node_to_cn_map = self._generate_fignode_to_cn_map() + +# # Step 2 - Generate edges for between the construction node with the biggest +# # fig_cover and the rest of the nodes +# for fig_node in fig_node_to_cn_map: +# cn_list = fig_node_to_cn_map[fig_node] +# # Get the construction node with the biggest fig_cover +# cn_with_biggest_fig_cover = max(cn_list, key=lambda x: len(x.fig_cover)) +# # Get the rest of the construction nodes +# cn_list.remove(cn_with_biggest_fig_cover) +# # Generate edges between the construction nodes +# for cn in cn_list: +# self.add_edge(cn_with_biggest_fig_cover.ID, cn.ID) + +# # Step 3 - Generate edges between the construction nodes that have fig nodes +# # that are neighbors of each other +# # Utilize the networkx.Graph.neighbors method to get the neighbors of each fig +# # node in the fignode map +# for fig_node in fig_node_to_cn_map: +# fig_node_neighbors = self._fig.neighbors(fig_node) +# for fig_node_neighbor in fig_node_neighbors: +# # Get the construction nodes that have the neighbor fig node +# cn_list = fig_node_to_cn_map[fig_node_neighbor] +# # Get the construction nodes that have the original fig node +# cn_list_with_fig_node = fig_node_to_cn_map[fig_node] +# # Generate edges between the construction nodes +# for cn in cn_list_with_fig_node: +# for cn_neighbor in cn_list: +# self.add_edge(cn.ID, cn_neighbor.ID) + +# def _generate_fignode_to_cn_map(self): +# fig_node_to_cn_map = {} +# for cn in self._construction_nodes: +# for fig_node in cn.fig_subgraph.nodes: +# if fig_node.ID not in fig_node_to_cn_map: +# fig_node_to_cn_map[fig_node.ID] = [] +# fig_node_to_cn_map[fig_node.ID].append(cn) +# return fig_node_to_cn_map + + +# def __generate_edge_between_passthrough_nodes( +# construction_graph: ConstructionGraph, +# sub, +# ) -> None: +# # Get the fig node to cn map +# fig_node_to_cn_map = construction_graph: ConstructionGraph._generate_fignode_to_cn_map() +# # Generate a pass through construciton node +# # If it getting till here we know for a fact that the subgraph is a passthrough +# # and has 1 input and 1 output node +# # Get the in node and the out node +# in_node = None +# out_node = None +# for node in list(sub.nodes): +# if sub.in_degree(node) == 0: +# in_node = node +# if sub.out_degree(node) == 0: +# out_node = node + +# # Find the neighbooring fig nodes +# if in_node is None or out_node is None: +# raise ValueError( +# "In and out nodes are not found, cannot apply passthrough connection in" +# " construction graph, check passthrough candidate identification logic" +# ) +# in_neighbors = list(sub.neighbors(in_node)) +# out_neighbors = list(sub.neighbors(out_node)) +# # Find the corresponding construction nodes +# in_cn_list = fig_node_to_cn_map[in_neighbors[0].ID] +# out_cn_list = fig_node_to_cn_map[out_neighbors[0].ID] +# # If construction nodes are the same, throw and error since we can cant have +# # passthrough if its looping around +# for in_cn in in_cn_list: +# if in_cn in out_cn_list: +# raise ValueError( +# "Encountered situation where in_cn is also out_cn, cannot have construction_graph: ConstructionGraph" +# " loops" +# ) +# # Now for each of the cases 1->1 , 1->n, n->1, n->n, n->m cases generate +# # connections +# if len(in_cn_list) == 1 and len(out_cn_list) == 1: +# # 1->1 +# self.add_edge(in_cn_list[0], out_cn_list[0]) +# elif len(in_cn_list) == 1 and len(out_cn_list) > 1: +# # 1->n +# for out_cn in out_cn_list: +# self.add_edge(in_cn_list[0], out_cn) +# elif len(in_cn_list) > 1 and len(out_cn_list) == 1: +# # n->1 +# for in_cn in in_cn_list: +# self.add_edge(in_cn, out_cn_list[0]) +# elif len(in_cn_list) > 1 and len(out_cn_list) == len(in_cn_list): +# # n->n +# for in_cn, out_cn in zip(in_cn_list, out_cn_list): +# self.add_edge(in_cn, out_cn) +# elif ( +# len(in_cn_list) > 1 +# and len(out_cn_list) > 1 +# and len(in_cn_list) != len(out_cn_list) +# ): +# # n->m +# for in_cn in in_cn_list: +# for out_cn in out_cn_list: +# self.add_edge(in_cn, out_cn) + +# raise NotImplementedError() + + +# def generate_construction_graph_edges(construction_graph: ConstructionGraph) -> None: +# """ +# This method generates the connections between the nodes in the graph. +# """ +# # Check if the FIG cover of neighboring construction nodes +# # and generate connection candidates +# self._bridge_channel_networks() +# # Step 1 - Generate a map where the key is a fig node and the value is a list of +# # construction nodes that have a fig node in their fig_subgraph +# fig_node_to_cn_map = self._generate_fignode_to_cn_map() +# # Step 2 - Generate edges for between the construction node with the biggest +# # fig_cover and the rest of the nodes +# for fig_node in fig_node_to_cn_map: +# cn_list = fig_node_to_cn_map[fig_node] +# # Get the construction node with the biggest fig_cover +# cn_with_biggest_fig_cover = max(cn_list, key=lambda x: len(x.fig_cover)) +# # Get the rest of the construction nodes +# cn_list.remove(cn_with_biggest_fig_cover) +# # Generate edges between the construction nodes +# for cn in cn_list: +# self.add_edge(cn_with_biggest_fig_cover.ID, cn.ID) + +# # Step 3 - Generate edges between the construction nodes that have fig nodes +# # that are neighbors of each other +# # Utilize the networkx.Graph.neighbors method to get the neighbors of each fig +# # node in the fignode map +# for fig_node in fig_node_to_cn_map: +# fig_node_neighbors = self._fig.neighbors(fig_node) +# for fig_node_neighbor in fig_node_neighbors: +# # Get the construction nodes that have the neighbor fig node +# cn_list = fig_node_to_cn_map[fig_node_neighbor] +# # Get the construction nodes that have the original fig node +# cn_list_with_fig_node = fig_node_to_cn_map[fig_node] +# # Generate edges between the construction nodes +# for cn in cn_list_with_fig_node: +# for cn_neighbor in cn_list: +# self.add_edge(cn.ID, cn_neighbor.ID) + + +# def _generate_fignode_to_cn_map(construction_graph: ConstructionGraph): +# fig_node_to_cn_map = {} +# for cn in construction_graph.construction_nodes: +# for fig_node in cn.fig_subgraph.nodes: +# if fig_node.ID not in fig_node_to_cn_map: +# fig_node_to_cn_map[fig_node.ID] = [] +# fig_node_to_cn_map[fig_node.ID].append(cn) +# return fig_node_to_cn_map + + +def check_overlap_criteria_1( + construction_node_a: ConstructionNode, construction_node_b: ConstructionNode +) -> bool: + """ + This method checks if the two construction nodes overlap in the following + criteria: + 1. if the the two nodes have any common fig nodes + 2. if the overlapping fig nodes are border nodes + """ + cover_a = set(construction_node_a.fig_subgraph.nodes) + cover_b = set(construction_node_b.fig_subgraph.nodes) + overlap_nodes_set = cover_a.intersection(cover_b) + + # TODO - Figure if we need to check if the overlapping nodes are border nodes + return len(overlap_nodes_set) > 0 + + +def check_adjecent_criteria_1( + construction_node_a: ConstructionNode, + construction_node_b: ConstructionNode, + fig: FluidInteractionGraph, +) -> bool: + """ + This method checks if the two construction nodes are adjacent in the following + criteria: + + Args: + construction_node_a (ConstructionNode): The first construction node + construction_node_b (ConstructionNode): The second construction node + + Returns: + bool: True if the two nodes are adjacent, False otherwise + """ + cover_a = set(construction_node_a.fig_subgraph.nodes) + cover_b = set(construction_node_b.fig_subgraph.nodes) + for fig_node_a in cover_a: + for fig_node_b in cover_b: + if fig_node_b in fig.neighbors(fig_node_a): + return True + + return False + + +def generate_construction_graph_edges( + fig: FluidInteractionGraph, construction_graph: ConstructionGraph +) -> None: + # The construction nodes that have overlaps (border nodes) with each other or are + # neighbors and make the connection between them. + + # Go through all the construction nodes + cn_nodes = list(construction_graph.nodes) + for i in range(len(cn_nodes)): + for j in range(len(cn_nodes)): + if i == j: + continue + source_node_id = cn_nodes[i] + source_cn_node = construction_graph.get_construction_node(source_node_id) + + target_node_id = cn_nodes[j] + target_cn_node = construction_graph.get_construction_node(target_node_id) + + # Check if they overlap + is_overlapped = check_overlap_criteria_1(source_cn_node, target_cn_node) + + if is_overlapped: + # Check if the two nodes are already connected + if construction_graph.has_edge( + source_node_id, target_node_id + ) or construction_graph.has_edge(target_node_id, source_node_id): + print( + f"Nodes {source_node_id}, {target_node_id} has edge, skipping" + " adding edge" + ) + continue + + construction_graph.connect_nodes(source_cn_node, target_cn_node) + continue + + # Check if they are neighbors + is_neighbor = check_adjecent_criteria_1(source_cn_node, target_cn_node, fig) + + if is_neighbor: + if construction_graph.has_edge( + source_node_id, target_node_id + ) or construction_graph.has_edge(target_node_id, source_node_id): + print( + f"Nodes {source_node_id}, {target_node_id} has edge, skipping" + " adding edge" + ) + continue + + construction_graph.connect_nodes(source_cn_node, target_cn_node) + continue diff --git a/lfr/netlistgenerator/constructiongraph/variant_criteria.py b/lfr/netlistgenerator/constructiongraph/variant_criteria.py new file mode 100644 index 0000000..a3efd27 --- /dev/null +++ b/lfr/netlistgenerator/constructiongraph/variant_criteria.py @@ -0,0 +1,25 @@ +from enum import Enum +from typing import Tuple + +from lfr.netlistgenerator.constructiongraph.constructiongraph import ConstructionGraph +from lfr.netlistgenerator.constructiongraph.constructionnode import ConstructionNode + + +class VariantType(Enum): + SUBSTITUTION = 1 + ADDITION = 2 + + +def check_variant_criteria( + variant: ConstructionGraph, node: ConstructionNode +) -> VariantType: + # Check if the node's fig mapping overlaps with the fig cover of the + # existing construction nodes according to the axioms definined. If it does + # return ADDITION, else return SUBSTITUTION. + for cn in variant._construction_nodes: + if cn.fig_cover == node.fig_cover: + return VariantType.SUBSTITUTION + elif node.has_border_overlap(cn): + return VariantType.ADDITION + else: + return VariantType.ADDITION diff --git a/lfr/netlistgenerator/constructiongraph/variant_generator.py b/lfr/netlistgenerator/constructiongraph/variant_generator.py new file mode 100644 index 0000000..5a64f05 --- /dev/null +++ b/lfr/netlistgenerator/constructiongraph/variant_generator.py @@ -0,0 +1,225 @@ +from typing import Dict, FrozenSet, List, Optional, Set, Tuple + +from lfr.fig.fluidinteractiongraph import FluidInteractionGraph +from lfr.netlistgenerator import LibraryPrimitivesEntry +from lfr.netlistgenerator.constructiongraph.constructiongraph import ConstructionGraph +from lfr.netlistgenerator.constructiongraph.constructionnode import ConstructionNode +from lfr.netlistgenerator.constructiongraph.variant_criteria import ( + VariantType, + check_variant_criteria, +) +from lfr.netlistgenerator.constructiongraph.varianttree import VariantTree +from lfr.netlistgenerator.gen_strategies.genstrategy import GenStrategy +from lfr.netlistgenerator.mappinglibrary import MappingLibrary, MatchPatternEntry +from lfr.netlistgenerator.namegenerator import NameGenerator +from lfr.postprocessor.mapping import NodeMappingTemplate + + +def generate_match_variants( + matches: List[LibraryPrimitivesEntry], + fig: FluidInteractionGraph, + library: MappingLibrary, + active_strategy: GenStrategy, + explicit_mapping_covers: Optional[List[Set[str]]] = None, +) -> List[ConstructionGraph]: + """ + Generate all possible match variants of a given graph. + + Args: + fig: The graph to generate match variants for. + + Returns: + A tree view of the matches + """ + + def find_index_of_match_entry( + fig_cover_set: Set[str], + matches: List[LibraryPrimitivesEntry], + ) -> int: + """Find the index of a match entry in a list of matches.""" + for index, entry in enumerate(matches): + if fig_cover_set == set(list(entry[2].keys())): + return index + raise KeyError("Could not find the index of the match entry") + + def move_entry_to_front( + entries_list: List[LibraryPrimitivesEntry], + index: int, + ) -> None: + """Move an entry to the front of a list.""" + entries_list.insert(0, entries_list.pop(index)) + + def generate_match_subgraph(match: LibraryPrimitivesEntry): + """Generate a fig subgraph view of a match.""" + nodes_list = list(match[2].keys()) + return fig.subgraph(nodes_list) + + def create_variant( + node: ConstructionNode, + variant: ConstructionGraph, + fig_node_cover: FrozenSet[str], + ): + print(f"Generating new variant for substitution {node.ID}") + new_variant = variant.generate_variant(f"variant_{variant_index}") + + # TODO - Substitute the node with the new node + new_variant.remove_node_for_exact_fig_cover(fig_node_cover) + new_variant.add_construction_node(node) + # Creates a new variant branch and add it to the variant tree + print(f"Adding node {node.ID} to variant {variant.ID}") + + variant_tree.add_variant(variant, new_variant) + + variant_tree: VariantTree[ConstructionGraph] = VariantTree[ConstructionGraph]() + cn_name_generator = NameGenerator() + # sort the matches based on the size of the dict keys + matches.sort(key=lambda x: len(x[1])) + + # Step 1 - First group all the matches with the same node mapping + # Step 2 - Sort earch group by the size of the dict keys (i.e. the number of nodes) + # Step 3 - Start with the smallest and find all the non overlapping matches sets. + # This, allows us to reduce the number of variants generated. + # there. + # Step 4 - We create a Variant Tree for each of the non overlapping match sets. + # We can use a greedy set packing problem to solve this. + # Step 5 - We create a Variant Tree for each of the non overlapping match sets. + + # TODO - take all the matches and start creating construction graphs and variants + # Loop trhough each of the matches + variant_index = 0 + + # Create the first variant here + seed_variant = ConstructionGraph(f"variant_{variant_index}", fig) + variant_tree.add_variant(seed_variant, None) + + # Find the set of matches that are non overlapping using a greedy cover algorithm + subsets = [] + universe = set() + + for match in matches: + nodes_set = set(list(match[2].keys())) + if nodes_set not in subsets: + subsets.append(nodes_set) + universe.union(nodes_set) + + # Group all the matches based on the node mappings + + # Order all the matches based on: + # 1. Explicit Matches + # 2. Set Cover Matches + # 3. Smallest Cover Matches + cover = generate_set_cover(universe, subsets) + print("Moving Max Set Cover to the front:", cover) + for cover_set_entry in cover: + move_entry_to_front( + matches, find_index_of_match_entry(cover_set_entry, matches) + ) + + if explicit_mapping_covers is not None: + # Moving all the explicit matches to the front + print("Moving Explicit Matches to the front:", explicit_mapping_covers) + for explicit_mapping_cover in explicit_mapping_covers: + move_entry_to_front( + matches, find_index_of_match_entry(explicit_mapping_cover, matches) + ) + + # Loop through every match + for match in matches: + print("Checking Match:", match) + # For each match, create a construction node + primitive_uid = match[0] + primitive_technology = match[1] + node = ConstructionNode( + cn_name_generator.generate_name(f"cn_{primitive_technology}"), + library.get_primitive(primitive_uid), + generate_match_subgraph(match), + ) + + fig_nodes_set = frozenset(list(match[2].keys())) + + # Check if Variant Tree has a saved divergence for this node, we do a new + # divergence branch and create a new variant. + divergence_node_payload = variant_tree.get_divergence(fig_nodes_set) + if divergence_node_payload is not None: + # Create the new variant and continue with the next match + variant_index += 1 + create_variant(node, divergence_node_payload, fig_nodes_set) + continue + + # Check if the construction node satisfies the variant criteria for each of the + # variants + for variant in variant_tree.walk_tree(): + variant_type = check_variant_criteria(variant, node) + + # VARIANT_TYPE.SUBSTITUTION + if variant_type is VariantType.ADDITION: + # Just add the node to the variant + print(f"Adding node {node.ID} to variant {variant.ID}") + variant.add_construction_node(node) + elif variant_type is VariantType.SUBSTITUTION: + # Create a new variant and add the node to it + variant_index += 1 + create_variant(node, variant, fig_nodes_set) + # Save Divergence in the Variant Tree for the corresponding delta + variant_tree.save_divergence(fig_nodes_set, variant) + else: + raise ValueError(f"Unknown variant type {variant_type}") + + # Next steps: + # 1. Find ways to map the flow elements to the construction nodes + # 2. Do the pruning of the variant tree utilizing the active strategy + # 3. Eliminate all the undercovered variants + + # Print out all the nodes in each of the variants + ret = variant_tree.walk_tree() + for variant in ret: + print(f"Variant {variant.ID}:") + for node in variant.nodes: + print(node) + return ret + + +def generate_set_cover(universe: Set[str], subsets: List[Set[str]]) -> List[Set[str]]: + """Find a family of subsets that covers the universal set + + Code replicated from: + http://www.martinbroadhurst.com/greedy-set-cover-in-python.html + """ + elements = set(e for s in subsets for e in s) + # Check the subsets cover the universe + # if elements != universe: + # return None + covered = set() + cover = [] + # Greedily add the subsets with the most uncovered points + while covered != elements: + subset = max(subsets, key=lambda s: len(s - covered)) + cover.append(subset) + covered |= subset + + return cover + + +# def add_construction_node( +# self, construction_node: ConstructionNode, variant_type: VariantType +# ) -> None: + +# # TODO - Just add the construction node into the graph +# if variant_type == VariantType.SUBSTITUTION: +# # Remove the existing construction node that has an intersecting fig cover +# # with the new construction node +# for cn in self._construction_nodes: +# if cn.fig_cover.intersection(construction_node.fig_cover): +# self.remove_construction_node(cn) +# break +# else: +# raise ValueError( +# "No construction node found with an intersecting fig cover" +# ) +# self._construction_nodes.append(construction_node) +# self.add_node(construction_node.ID) +# elif variant_type == VariantType.ADDITION: +# self._construction_nodes.append(construction_node) +# self.add_node(construction_node.ID) +# else: +# raise ValueError("Invalid variant type") diff --git a/lfr/netlistgenerator/constructiongraph/varianttree.py b/lfr/netlistgenerator/constructiongraph/varianttree.py new file mode 100644 index 0000000..7709c5e --- /dev/null +++ b/lfr/netlistgenerator/constructiongraph/varianttree.py @@ -0,0 +1,236 @@ +from __future__ import annotations + +from typing import Dict, Generic, List, Optional, TypeVar + +T = TypeVar("T") + + +class VariantNode(Generic[T]): + """Variant Node Describes the node in the variant tree + + Its payload is a ConstructionGraph object and it contains the links to the parent + and children + """ + + def __init__(self, variant: T) -> None: + self._payload: T = variant + self._parent: Optional[VariantNode] = None + self._children: List[VariantNode] = [] + + @property + def payload(self) -> T: + return self._payload + + @property + def children(self) -> List[VariantNode]: + return self._children + + @property + def parent(self) -> Optional[VariantNode]: + return self._parent + + @parent.setter + def parent(self, parent: VariantNode) -> None: + self._parent = parent + + def add_child(self, child: VariantNode) -> None: + self._children.append(child) + child._parent = self + + def __eq__(self, __o: object) -> bool: + if isinstance(__o, VariantNode): + # Check if the payloads are equal + return self._payload == __o._payload + else: + return False + + +class VariantTree(Generic[T]): + def __init__( + self, + ) -> None: + self._root: Optional[VariantNode] = None + self._node_walk: Optional[VariantNode] = None + self._divergence_map: Dict[object, VariantNode] = {} + + def add_variant( + self, + variant_parent: T, + variant_decendant: Optional[T], + ) -> None: + """Adds a new variant to the tree + + When adding the root node, the variant_leaf is set to None + + Args: + variant_parent (ConstructionGraph): The parent variant to which we say that, + when the variant tree is empty, this is the root. + variant_decendant (Optional[ConstructionGraph]): The new variant to add to + the tree, its set to None when adding the root. + + Raises: + ValueError: The variant_parent is not in the tree, or the variant_decendant + is set to None when we are not setting the root + """ + if self._root is None: + root = VariantNode(variant_parent) + self._root = root + + if variant_decendant is not None: + leaf = VariantNode(variant_decendant) + leaf.parent = root + self._root.add_child(leaf) + + else: + # Cancel if only the variant root is provided + if variant_decendant is None: + raise ValueError("variant_decendant is None") + + # Find the parent of the variant and add the variant as the child + parent = self._find_node(variant_parent) + if parent is None: + raise ValueError(f"Could not find parent of variant {variant_parent}") + + leaf = VariantNode(variant_decendant) + leaf.parent = parent + parent.add_child(leaf) + + def _find_node(self, variant: T) -> Optional[VariantNode]: + """Find the node in the tree that matches the variant""" + if self._root is None: + return None + + # Perform recursive search to identify the variant provided + # If the variant is found, return the node + # If the variant is not found, return None + + def _find_node_recursive( + node: VariantNode, variant: T + ) -> Optional[VariantNode]: + if node.payload == variant: + return node + + for child in node.children: + result = _find_node_recursive(child, variant) + if result is not None: + return result + + return None + + return _find_node_recursive(self._root, variant) + + def prune_variant(self, variant: T) -> None: + """Prune the variant tree to remove any variants that do not fully cover the + fig""" + + def _prune_variant_recursive(node: VariantNode, variant: T) -> None: + if node.payload == variant: + # If the variant is found, remove the node from the tree + if node.parent is not None: + node.parent.children.remove(node) + return + + for child in node.children: + _prune_variant_recursive(child, variant) + + if self._root is not None: + # Check the trivial case if the root is the variant + if self._root.payload == variant: + self._root = None + return + + _prune_variant_recursive(self._root, variant) + + def get_edge_leaves(self) -> List[T]: + """Return all the ConstructionGraphs in the leaves of the variant tree""" + leaves: List[T] = [] + + def _edge_leaves_recursive(node: VariantNode) -> None: + if len(node.children) == 0: + leaves.append(node.payload) + return + + for child in node.children: + _edge_leaves_recursive(child) + + if self._root is not None: + _edge_leaves_recursive(self._root) + + return leaves + + def walk_tree(self) -> List[T]: + """Return an iterator that walks the variant tree + + Raises: + ValueError: If the root is not set + + Returns: + List[ConstructionGraph]: List of ConstructionGraphs that are in the variant + """ """""" + + def _walker_recursive(level_nodes: List[VariantNode], output: List[T]) -> None: + next_level_nodes: List[VariantNode] = [] + for level_node in level_nodes: + for child in level_node.children: + next_level_nodes.append(child) + output.append(child.payload) + + for level_node in level_nodes: + _walker_recursive(next_level_nodes, output) + + if self._root is None: + raise ValueError("Variant tree root is empty") + + node_walk: List[T] = [self._root.payload] + _walker_recursive([self._root], node_walk) + return node_walk + + def has_divergence(self, divergence: object) -> bool: + """Check if the variant has divergence + + Args: + divergence (object): divergence object that needs to be checked + + Returns: + bool: True if the divergence is found, False otherwise + """ + if divergence in self._divergence_map: + return True + else: + return False + + def save_divergence(self, divergence: object, variant: T) -> None: + """Save the divergence of the variant to the variant tree + + Args: + divergence (str): The delta that is being saved as the cause of the + divergence + variant (T): the payload corresponding to the divergence + + Raises: + ValueError: If the payload cannot be found in the tree + ValueError: If the divergence is already saved + """ + node = self._find_node(variant) + if node is None: + raise ValueError(f"Could not find variant {variant}") + + if divergence in self._divergence_map: + raise ValueError(f"Divergence {divergence} already exists") + + self._divergence_map[divergence] = node + + def get_divergence(self, divergence: object) -> Optional[T]: + """Get the payload corresponding to the divergence + + Args: + divergence (object): object indicating the divergence + + Returns: + Optional[T]: the payload corresponding to the divergence + """ + + if divergence in self._divergence_map: + return self._divergence_map[divergence].payload + else: + return None diff --git a/lfr/netlistgenerator/dafdadapter.py b/lfr/netlistgenerator/dafdadapter.py index b7a2f9c..dbef1d3 100644 --- a/lfr/netlistgenerator/dafdadapter.py +++ b/lfr/netlistgenerator/dafdadapter.py @@ -1,8 +1,10 @@ -from lfr.postprocessor.constraints import Constraint -from typing import List -from pymint.mintcomponent import MINTComponent +from typing import Dict, List + +from parchmint import Component from pymint.mintdevice import MINTDevice +from lfr.postprocessor.constraints import Constraint + class DAFDAdapter: def __init__(self, device: MINTDevice) -> None: @@ -10,7 +12,7 @@ def __init__(self, device: MINTDevice) -> None: self._device = device def size_droplet_generator( - self, component: MINTComponent, constriants: List[Constraint] + self, component: Component, constriants: List[Constraint] ) -> None: import faulthandler @@ -21,20 +23,27 @@ def size_droplet_generator( self.solver = DAFD_Interface() # TODO: Check the type of the component and pull info from DAFD Interface targets_dict = {} - constriants_dict = {} + constriants_dict: Dict[str, Constraint] = {} for constraint in constriants: if constraint.key == "volume": # r≈0.62035V1/3 volume = constraint.get_target_value() - targets_dict["droplet_size"] = float(volume) ** 0.33 * 0.62035 * 2 + if volume is None: + raise ValueError( + "Could not retrieve target value from constraint : volume for" + f" component:{component.ID}" + ) + targets_dict["droplet_size"] = volume**0.33 * 0.62035 * 2 elif constraint.key == "generation_rate": generate_rate = constraint.get_target_value() targets_dict["generation_rate"] = generate_rate else: raise Exception("Error: Geometry constraint not defined") - results = self.solver.runInterp(targets_dict, constriants_dict) + results = self.solver.runInterp( + targets_dict, constriants_dict, tolerance_test=True + ) if component is None: raise Exception("No component attached to the constraints") diff --git a/lfr/netlistgenerator/explicitmapping.py b/lfr/netlistgenerator/explicitmapping.py index 25d0caf..fb25d5a 100644 --- a/lfr/netlistgenerator/explicitmapping.py +++ b/lfr/netlistgenerator/explicitmapping.py @@ -6,9 +6,15 @@ class ExplicitMappingType(Enum): FLUID_INTERACTION = 0 STORAGE = 1 NETWORK = 2 + PUMP = 3 class ExplicitMapping: + """Explicit Mapping + + This is the main class that is used to map micorfluidic technologies to a fluid interaction graph. + """ + def __init__( self, mapping_type: ExplicitMappingType = ExplicitMappingType.FLUID_INTERACTION ): @@ -39,4 +45,4 @@ def map(self, netlist, fig): # start and end and remove the correspoinding connections. # TODO: In scenarios where there are inbetween nodes, we probably need to be more careful and # this might not be the right place to do that kind of mapping - pass + raise NotImplementedError() diff --git a/lfr/netlistgenerator/explicitmappingoption.py b/lfr/netlistgenerator/explicitmappingoption.py index 43a8091..6e7e02b 100644 --- a/lfr/netlistgenerator/explicitmappingoption.py +++ b/lfr/netlistgenerator/explicitmappingoption.py @@ -1,7 +1,8 @@ -from lfr.postprocessor.constraints import Constraint from typing import List + from lfr.netlistgenerator.mappingoption import MappingOption from lfr.netlistgenerator.networkmappingoption import NetworkMappingOption +from lfr.postprocessor.constraints import Constraint class ExplicitMappingOption: diff --git a/lfr/netlistgenerator/flownetworkmatching.py b/lfr/netlistgenerator/flownetworkmatching.py new file mode 100644 index 0000000..bd1c268 --- /dev/null +++ b/lfr/netlistgenerator/flownetworkmatching.py @@ -0,0 +1,89 @@ +from typing import List + +import networkx as nx + +from lfr.fig.fluidinteractiongraph import FluidInteractionGraph +from lfr.netlistgenerator.constructiongraph.constructiongraph import ConstructionGraph +from lfr.netlistgenerator.constructiongraph.constructionnode import ConstructionNode +from lfr.netlistgenerator.gen_strategies.genstrategy import GenStrategy +from lfr.netlistgenerator.networkmappingoption import ( + NetworkMappingOption, + NetworkMappingOptionType, +) +from lfr.netlistgenerator.primitive import NetworkPrimitive + + +def add_flow_flow_matching_candidates( + fig: FluidInteractionGraph, + variants: List[ConstructionGraph], + gen_strategy: GenStrategy, +) -> None: + flow_cns = get_flow_flow_candidates(fig, gen_strategy) + print("Found New Flow-Flow match nodes:", flow_cns) + add_flow_flow_candadates_to_variants(variants, flow_cns) + print("New variant Mappings:", [str(v) for v in variants]) + + +def add_flow_flow_candadates_to_variants( + variants: List[ConstructionGraph], flow_cns: List[ConstructionNode] +) -> None: + # TODO: Map the flow flow candidates to the variants + # raise NotImplementedError("Need to implement this") + print( + "Warning: FLow Flow Mapping to variants not implemented yet since it might" + " generate network primitives" + ) + pass + + +def get_flow_flow_candidates( + fig: FluidInteractionGraph, gen_strategy: GenStrategy +) -> List[ConstructionNode]: + # TODO - go through all the edges and see which ones are between flow-flow graphs + # If these connectsions are between flow-flow nodes then we need to figure out + # which ones are part of the same network/connected graphs with only flow nodes + # The networks with only the flow nodes will need to be covered as a part of. + # these construction nodes. + + ret = [] + + # Step 1. Do a shallow copy of the graph + # Step 2. Remove all the fignodes that are not Flow + # Step 3. Now get the all the disconnected pieces of the graph + # Step 4. Create a Construction node for each of the disconnected pieces + # Return all the constructions nodes + + # Step 1. Do a shallow copy of the graph + fig_original = fig + fig_copy = fig.copy( + as_view=False + ) # Note this does not copy anything besides the nx.DiGraph at the moment + + # Step 2. Remove all the fignodes that are not Flow + remove_list = [] + for node_id in fig_copy.nodes: + node = fig_original.get_fignode(node_id) + if node.match_string != "FLOW": + remove_list.append(node_id) + + for node_id in remove_list: + fig_copy.remove_node(node_id) + + # Step 3. Now get the all the disconnected pieces of the graph + i = 0 + for component in nx.connected_components(fig_copy.to_undirected()): + print("Flow candidate", component) + sub = fig_original.subgraph(component) + # TODO - Decide what the mapping type should be. for now assume that we just a single + # passthrough type scenario where we don't have to do much work + nprimitive = NetworkPrimitive(sub, gen_strategy) + nprimitive.generate_netlist() + # mapping_option = NetworkMappingOption(nprimitive, mapping_type, sub) + # Step 4. Create a Construction node for each of the disconnected pieces + # TODO - Check and see what happens here + cn = ConstructionNode("flow_network_{}".format(i), nprimitive, sub) + + i += 1 + ret.append(cn) + + return ret diff --git a/lfr/netlistgenerator/gen_strategies/dropxstrategy.py b/lfr/netlistgenerator/gen_strategies/dropxstrategy.py index 52b9552..9596709 100644 --- a/lfr/netlistgenerator/gen_strategies/dropxstrategy.py +++ b/lfr/netlistgenerator/gen_strategies/dropxstrategy.py @@ -1,212 +1,79 @@ +# from lfr.netlistgenerator.constructiongraph import ConstructionGraph +from typing import List + +import networkx as nx +from pymint import MINTDevice + +from lfr.fig.fignode import FIGNode +from lfr.fig.fluidinteractiongraph import FluidInteractionGraph from lfr.fig.interaction import Interaction, InteractionType +from lfr.netlistgenerator.constructiongraph.constructiongraph import ConstructionGraph +from lfr.netlistgenerator.constructiongraph.constructionnode import ConstructionNode from lfr.netlistgenerator.dafdadapter import DAFDAdapter -from lfr.fig.fluidinteractiongraph import FluidInteractionGraph -from lfr.netlistgenerator.constructiongraph import ConstructionGraph -from lfr.netlistgenerator.constructionnode import ConstructionNode from lfr.netlistgenerator.gen_strategies.genstrategy import GenStrategy -import networkx as nx -from pymint import MINTDevice class DropXStrategy(GenStrategy): - def __init__( - self, construction_graph: ConstructionGraph, fig: FluidInteractionGraph - ) -> None: - super().__init__(construction_graph, fig) - - def reduce_mapping_options(self) -> None: - # Generate a topological order for the FIG to make sure athat we know the order of things - figs_in_order = list(nx.topological_sort(self._fig)) - # Generate the mapping between fignodes and construction nodes - # input_fignodes = self._fig.get_input_fignodes - for fignode_id in self._fig.nodes: - fignode = self._fig.get_fignode(fignode_id) - - # check if construction node - if ConstructionNode(fignode.ID).is_explictly_mapped: - pass - # TODO - Implement Generalized Ali Strategy 1 - # Rule 1 - The first level of % should be mapping to a Droplet Generator - # (TODO - Talk to ali about this or a T junction generator) - # Step 1 - Check if fig node is interaction and of type METER (%) this is t - # he check condition for rule 1 - else: - if isinstance(fignode, Interaction): - if fignode.type is InteractionType.METER: - is_first_metering_node = True - # TODO - Check if DROPLET GENERATOR is one of the mapping - # options, skip if not - # Step 2 - If type is meter then check to see if other it is - # the first meter operation from inputs to current fignode - for sorted_fignode_id in figs_in_order: - if fignode_id == sorted_fignode_id: - # End of checking - if is_first_metering_node is True: - # TODO - Eliminate all options other than Nozzle - # droplet generator for the associated construction - # node(s) - cn = self._construction_graph.get_fignode_cn( - fignode - ) - # check mapping options - print("***Detect Nozzle Droplet Gen***") - for cn_part in cn.mapping_options: - if ( - cn_part.primitive.mint - != "NOZZLE DROPLET GENERATOR" - ): - # remove if its not nozzle droplet generator - cn.mapping_options.remove(cn_part) - else: - raise Exception( - "No scheme for assign METER after initial" - " droplet generation" - ) - else: - # get the sorted fignode - sorted_fignode = self._fig.get_fignode( - sorted_fignode_id - ) - if isinstance(sorted_fignode, Interaction): - if sorted_fignode.type is InteractionType.METER: - # check if node is connected to our node of - # interest - if sorted_fignode_id in self._fig.predecessors( - fignode_id - ): - is_first_metering_node = False - - # Rule 2 – Any +-, distribute nodes before % should be in continuous flow - # (figure out components for this) - # TODO - Go through each of the FIG nodes, if the fig node has - - for fignode_id in self._fig.nodes: - fignode = self._fig.get_fignode(fignode_id) - - # check if explicitly mapped - if not ConstructionNode(fignode.ID).is_explictly_mapped: - if isinstance(fignode, Interaction): - if ( - fignode.type is InteractionType.MIX - or fignode.type is InteractionType.SIEVE - ): - if self.__check_continuous(fignode_id): - # this is NOT continuous - raise Exception("flow before METER is not continuous") - - # Rule 3 – Any Remetering (%) should require a droplet breakdown and - # regeneration (Ask Ali) - # Rule 4 – Distribute network post Metering stage should be mapped to different - # kinds of separator / selection/ storage networks - # Rule 5 – If plus is shown between node that has % in pred and non % in pred, t - # hen its pico injection - - for fignode_id in self._fig.nodes: - fignode = self._fig.get_fignode(fignode_id) - - # check if map - if not ConstructionNode(fignode.ID).is_explictly_mapped: - if isinstance(fignode, Interaction): - # if + - if ( - fignode.type is InteractionType.MIX - or fignode.type is InteractionType.SIEVE - ): - # compare predecessors, not successors - # create warning for more than 2 (mix can have more than 2 inputs) - - meter_in_pred = [] - - # check pred - - for prednode_id in self._fig.predecessors(fignode_id): - # check the first pred - - meter_in_pred.append( - self.__search_predecessors( - prednode_id, InteractionType.METER - ) - ) - - numTrue = meter_in_pred.count(True) - - if fignode.type is InteractionType.MIX: - - if numTrue == 1: - # this is a pico injection - cn = self._construction_graph.get_fignode_cn(fignode) - print("***Detect Pico Injector***") - # check mapping options - while self.__exist_in_cn(cn, "PICOINJECTOR"): - for cn_part in cn.mapping_options: - print("-", cn_part.primitive.mint) - if cn_part.primitive.mint != "PICOINJECTOR": - # remove if its not [ico injection - cn.mapping_options.remove(cn_part) - print("after----") - - for cn_part in cn.mapping_options: - print("-", cn_part.primitive.mint) - - # Rule 6 - elif numTrue == 2: - # this is a droplet merging - cn = self._construction_graph.get_fignode_cn(fignode) - - print("***Detect Droplet Merger***") - while self.__exist_in_cn(cn, "DROPLET MERGER"): - for cn_part in cn.mapping_options: - print("-", cn_part.primitive.mint) - if cn_part.primitive.mint != "DROPLET MERGER": - cn.mapping_options.remove(cn_part) - print("after----") - - for cn_part in cn.mapping_options: - print("-", cn_part.primitive.mint) + def __init__(self, fig: FluidInteractionGraph) -> None: + super().__init__("dropx", fig) - else: - print("***Mixer***") - cn = self._construction_graph.get_fignode_cn(fignode) - while self.__exist_in_cn(cn, "MIXER"): - for cn_part in cn.mapping_options: - print("-", cn_part.primitive.mint) - if cn_part.primitive.mint != "MIXER": - cn.mapping_options.remove(cn_part) - - print("after------") - for cn_part in cn.mapping_options: - print("-", cn_part.primitive.mint) - pass + def validate_construction_graph_flow( + self, construction_graph: ConstructionGraph + ) -> bool: + """ + Validate the construction graph against the rules of the dropx + generation strategy strategy - # Rule 6 – if plus is sown between two nodes that has % in pred, then its - # droplet merging - # Rule 7 – TBD Rule for droplet splitting - # Finally just reduce the total number of mapping options if greater than 1 - super().reduce_mapping_options() + TODO - Future version of this should use a rule based grammar - @staticmethod - def __exist_in_cn(cn, mint_name): - """helper function to check if the construction node contains undesired mints. + Current ruleset for the dropx strategy: can be found in the notes Args: - cn (ConstructionNode): ConstructionNode to be checked - mint_name (string): mint name to check + construction_graph (ConstructionGraph): Construction graph to validate Returns: - Bool: if mint names other than the specified mint_name is found, returns true. Else false. + bool: True if the construction graph is valid """ - for cn_part in cn.mapping_options: - if cn_part.primitive.mint != mint_name: - return True - - return False - - def __search_predecessors(self, fignode_id, search_type): + # TODO - + # Step 1 - Perform a topological sort of the fignodes so that we + # know how to traverse anc check the rules against each of the nodes + # Step 2 - For each of these fig nodes call the individual rules (all 6/7 of them). + # Step 3 - If response of any of those 7 is false then return false indicating that + # its an invalid design. Skip rulecheck if explicitly mapped + # + sorted_fignodes = list(nx.topological_sort(self._fig)) + for fignode in sorted_fignodes: + # TODO - Skip check if explicitly mapped. Rationale is that user knows best + rule_1_success = self.__check_rule_1_validity(fignode, construction_graph) + if rule_1_success is False: + return False + rule_2_success = self.__check_rule_2_validity(fignode, construction_graph) + if rule_2_success is False: + return False + rule_3_success = self.__check_rule_3_validity(fignode, construction_graph) + if rule_3_success is False: + return False + rule_4_success = self.__check_rule_4_validity(fignode, construction_graph) + if rule_4_success is False: + return False + rule_5_success = self.__check_rule_5_validity(fignode, construction_graph) + if rule_5_success is False: + return False + rule_6_success = self.__check_rule_6_validity(fignode, construction_graph) + if rule_6_success is False: + return False + rule_7_success = self.__check_rule_7_validity(fignode, construction_graph) + if rule_7_success is False: + return False + + return True + + def __search_predecessors(self, fignode_id: str, search_type: InteractionType): """recursive function searches for the specified InteractionType in the predecessors of the specified fignode_id Args: - fignode_id (elements in self._fig.nodes): Starting node to find the + fignode_id (str): Starting node to find the predecessors search_type (InteractionType): Interaction type to find in the predecessors @@ -225,8 +92,43 @@ def __search_predecessors(self, fignode_id, search_type): return False + def __check_rule_1_validity( + self, fignode: str, constructin_graph: ConstructionGraph + ) -> bool: + raise NotImplementedError() + + def __check_rule_2_validity( + self, fignode: str, constructin_graph: ConstructionGraph + ) -> bool: + raise NotImplementedError() + + def __check_rule_3_validity( + self, fignode: str, constructin_graph: ConstructionGraph + ) -> bool: + raise NotImplementedError() + + def __check_rule_4_validity( + self, fignode: str, constructin_graph: ConstructionGraph + ) -> bool: + raise NotImplementedError() + + def __check_rule_5_validity( + self, fignode: str, constructin_graph: ConstructionGraph + ) -> bool: + raise NotImplementedError() + + def __check_rule_6_validity( + self, fignode: str, constructin_graph: ConstructionGraph + ) -> bool: + raise NotImplementedError() + + def __check_rule_7_validity( + self, fignode: str, constructin_graph: ConstructionGraph + ) -> bool: + raise NotImplementedError() + @staticmethod - def __check_if_type(fignode, search_type): + def __check_if_type(fignode: FIGNode, search_type: InteractionType): """helper function for __search_predecessors and __check_continuous. Check if the specified search_type matches to the fignode.type @@ -242,45 +144,3 @@ def __check_if_type(fignode, search_type): return True return False - - # this function checks if the predecessors before METER has METER or not. If not, - # continuous. - def __check_continuous(self, fignode_id): - """recursive function to check if the flow before METER is continuous at MIX or SIEVE - - Args: - fignode_id (elements in self._fig.nodes): Starting node to find predecessors - - Returns: - Bool: if METER is found in the predecessors of METER, returns true - (not continuous), Else false - """ - fignode = self._fig.get_fignode(fignode_id) - - if self.__check_if_type(fignode, InteractionType.METER): - for prednode_id in self._fig.predecessors(fignode_id): - if self.__search_predecessors(prednode_id, InteractionType.METER): - return True - - else: - for other_pred_id in self._fig.predecessors(fignode_id): - if self.__check_continuous(other_pred_id): - return True - - return False - - def size_netlist(self, device: MINTDevice) -> None: - """ - Sizes the device based on either lookup tables, inverse design algorithms, etc. - """ - dafd_adapter = DAFDAdapter(device) - # Default size for PORT is 2000 um - for component in device.components: - constraints = self._construction_graph.get_component_cn( - component - ).constraints - if component.entity == "NOZZLE DROPLET GENERATOR": - # dafd_adapter.size_droplet_generator(component, constraints) - print("Skipping calling DAFD since its crashing everything right now") - elif component.entity == "PORT": - component.params.set_param("portRadius", 2000) diff --git a/lfr/netlistgenerator/gen_strategies/dummy.py b/lfr/netlistgenerator/gen_strategies/dummy.py index 6011a3b..6816d5b 100644 --- a/lfr/netlistgenerator/gen_strategies/dummy.py +++ b/lfr/netlistgenerator/gen_strategies/dummy.py @@ -1,18 +1,9 @@ from lfr.fig.fluidinteractiongraph import FluidInteractionGraph -from lfr.netlistgenerator.mappingoption import MappingOption from lfr.netlistgenerator.gen_strategies.genstrategy import GenStrategy -from lfr.netlistgenerator.constructiongraph import ConstructionGraph +from lfr.netlistgenerator.mappingoption import MappingOption +# from lfr.netlistgenerator.constructiongraph import ConstructionGraph -class DummyStrategy(GenStrategy): - def __init__( - self, construction_graph: ConstructionGraph, fig: FluidInteractionGraph - ) -> None: - super().__init__(construction_graph, fig) - def reduce_mapping_options(self) -> None: - super().reduce_mapping_options() - - @staticmethod - def get_flow_flow_mapping_option(subgraph_view) -> MappingOption: - return None +class DummyStrategy(GenStrategy): + pass diff --git a/lfr/netlistgenerator/gen_strategies/genstrategy.py b/lfr/netlistgenerator/gen_strategies/genstrategy.py index 75440dd..1d70a6d 100644 --- a/lfr/netlistgenerator/gen_strategies/genstrategy.py +++ b/lfr/netlistgenerator/gen_strategies/genstrategy.py @@ -9,117 +9,110 @@ if TYPE_CHECKING: from lfr.netlistgenerator.constructiongraph import ConstructionGraph -from lfr.netlistgenerator.connectingoption import ConnectingOption from typing import List +from parchmint import Target from pymint.mintdevice import MINTDevice from pymint.mintnode import MINTNode -from pymint.minttarget import MINTTarget + +from lfr.netlistgenerator.connectingoption import ConnectingOption class GenStrategy: - def __init__( - self, construction_graph: ConstructionGraph, fig: FluidInteractionGraph - ) -> None: - self._construction_graph: ConstructionGraph = construction_graph + def __init__(self, name: str, fig: FluidInteractionGraph) -> None: + self._name: str = name self._fig: FluidInteractionGraph = fig - self._fig_netlist_map: Dict[str, str] = {} + + def validate_construction_graph_flow( + self, construction_graph: ConstructionGraph + ) -> bool: + """ + Validate the construction graph against a set of rules + + TODO - Future version of this should use a rule based grammar + like Eugene but for topologically sorted FIGs where we + figure out where the outputs of a certain flow network + can be connected to the inputs of another flow network + (Needs to be put in theorem form). + + Args: + construction_graph (ConstructionGraph): Construction graph to validate + + Returns: + bool: True if the construction graph is valid + """ + raise NotImplementedError() def reduce_mapping_options(self) -> None: - # Dummy strategy - for cn in self._construction_graph.construction_nodes: - # print(len(cn.mapping_options)) - # clean this - # Remove the extra mappings - print( - "Reducing mapping options for Construction node: {} from {} to {}" - .format(cn.ID, len(cn.mapping_options), 1), - ) - if len(cn.mapping_options) > 1: - for option in cn.mapping_options: - print(" -{}".format(option.primitive.mint)) - del cn.mapping_options[1 : len(cn.mapping_options)] - # print("... -> {}".format(len(cn.mapping_options))) - - print("Printing all final mapping options:") - for cn in self._construction_graph.construction_nodes: - print("Construction node: {}".format(cn.ID)) - print("Options: ") - - for mapping_option in cn.mapping_options: - print(mapping_option.primitive.mint) - - def generate_flow_network(self, fig_subgraph_view) -> MINTDevice: - # TODO - For now just assume that the networks basically are a bunch - # of nodes with nets/channels connecting them - ret = MINTDevice("flow_network_temp") - mint_layer = ret.create_mint_layer("0", "0", 0, MINTLayerType.FLOW) - for node in fig_subgraph_view.nodes: - n = MINTNode("node_{}".format(node), mint_layer) - ret.add_component(n) - self._store_fig_netlist_name(node, n.ID) - - i = 1 - for node in fig_subgraph_view.nodes: - # Create the channel between these nodes - channel_name = "c_{}".format(i) - i += 1 - params = {} - params["channelWidth"] = 400 - source = MINTTarget("node_{}".format(node)) - sinks = [] - - # Add all the outgoing edges - for edge in fig_subgraph_view.out_edges(node): - tar = edge[1] - if tar not in fig_subgraph_view.nodes: - # We skip because this might be a weird edge that we were not supposed - # to have in this network - continue - - sinks.append(MINTTarget("node_{}".format(tar))) - - ret.create_mint_connection( - channel_name, "CHANNEL", params, source, sinks, "0" - ) - - return ret - - def _store_fig_netlist_name(self, fig_id: str, netlist_id: str) -> None: - self._fig_netlist_map[fig_id] = netlist_id - - def _get_fig_netlist_name(self, fig_id: str) -> str: - return self._fig_netlist_map[fig_id] - - def generate_input_connectingoptions(self, subgraph_view) -> List[ConnectingOption]: - subgraph_inputs = [] - for node in list(subgraph_view.nodes): - if len(subgraph_view.in_edges(node)) == 0: - subgraph_inputs.append( - ConnectingOption(self._get_fig_netlist_name(node)) - ) - - return subgraph_inputs - - def generate_output_connectingoptions( - self, subgraph_view - ) -> List[ConnectingOption]: - subgraph_outputs = [] - for node in list(subgraph_view.nodes): - if len(subgraph_view.out_edges(node)) == 0: - subgraph_outputs.append( - ConnectingOption(self._get_fig_netlist_name(node)) - ) - - return subgraph_outputs + """ + Reduce the mapping options for the construction graph + + TODO - In the future go through the options by looking at Rama + like apps that can be used for pruning technologies based on + the physics parameters. + + Raises: + NotImplementedError: If the method is not implemented + """ + raise NotImplementedError() + + def generate_flow_network(self) -> None: + """Generate the flow flow network mappings + + TODO - Use this to sort through the correct kinds of network primitives + to use for the flow-flow networks + """ + raise NotImplementedError() + + def generate_input_connectingoptions(self): + """Unsure if this is necessary in the future with the new architecture + + Raises: + NotImplementedError: Raises if the method is not implemented + """ + raise NotImplementedError() + + def generate_output_connectingoptions(self): + """Unsure if this is necessary in the future with the new architecture + + Raises: + NotImplementedError: Raises if the method is not implemented + """ + raise NotImplementedError() @staticmethod - def generate_carrier_connectingoptions(subgraph_view) -> List[ConnectingOption]: + def generate_carrier_connectingoptions(): + """ + Generate the connecting options for the carrier networks. + + TODO - In the future version, go through the connectivity of the flow network, + compute what options would need to be pipelined and then optimize the carrier + network to have the least number of carrier inputs and wastes. + """ return [] @staticmethod - def generate_loading_connectingoptions(subgraph_view) -> List[ConnectingOption]: - return [] + def generate_loading_connectingoptions(): + """ + Generate the connecting options for the loading networks. + + TODO - In the future version, go through the connectivity of the flow network, + compute what options would need to be pipelined and then optimize the loading + network to have the least number of carrier inputs and wastes. + + Args: + subgraph_view (_type_): _description_ + + Returns: + List[ConnectingOption]: _description_ + """ + raise NotImplementedError() def size_netlist(self, device: MINTDevice) -> None: raise NotImplementedError() + + def prune_variants(self, variants: List[ConstructionGraph]) -> None: + for variant in variants: + if variant.is_fig_fully_covered() is not True: + print(f"Removing variant (Construction Graph): {variant.ID}") + variants.remove(variant) diff --git a/lfr/netlistgenerator/gen_strategies/mars.py b/lfr/netlistgenerator/gen_strategies/mars.py deleted file mode 100644 index fa33a12..0000000 --- a/lfr/netlistgenerator/gen_strategies/mars.py +++ /dev/null @@ -1,22 +0,0 @@ -from lfr.netlistgenerator.constructiongraph import ConstructionGraph -from lfr.netlistgenerator.gen_strategies.genstrategy import GenStrategy - - -class MARSStrategy(GenStrategy): - def __init__(self, construction_graph: ConstructionGraph) -> None: - super().__init__(construction_graph) - - def reduce_mapping_options(self) -> None: - # TODO - Implement Generalized Ali Strategy 1 - - # Dummy strategy - for cn in [v for k, v in self._construction_nodes.items()]: - print(len(cn.mapping_options)) - # Remove the extra mappings - del cn.mapping_options[1 : len(cn.mapping_options)] - print(len(cn.mapping_options)) - pass - - @overload - def size_netlist(): - super() diff --git a/lfr/netlistgenerator/gen_strategies/marsstrategy.py b/lfr/netlistgenerator/gen_strategies/marsstrategy.py index bf0ac70..1caa895 100644 --- a/lfr/netlistgenerator/gen_strategies/marsstrategy.py +++ b/lfr/netlistgenerator/gen_strategies/marsstrategy.py @@ -2,158 +2,26 @@ from typing import TYPE_CHECKING, Dict -from networkx.generators.degree_seq import directed_configuration_model from pymint.mintlayer import MINTLayerType from lfr.fig.fluidinteractiongraph import FluidInteractionGraph +from lfr.netlistgenerator.constructiongraph.constructionnode import ConstructionNode from lfr.netlistgenerator.dafdadapter import DAFDAdapter -from lfr.netlistgenerator.constructionnode import ConstructionNode - -from typing import Dict, TYPE_CHECKING - -from pymint.mintlayer import MINTLayerType +from lfr.netlistgenerator.gen_strategies.genstrategy import GenStrategy if TYPE_CHECKING: - from lfr.netlistgenerator.constructiongraph import ConstructionGraph + from lfr.netlistgenerator.constructiongraph.constructiongraph import ( + ConstructionGraph, + ) -from lfr.netlistgenerator.connectingoption import ConnectingOption from typing import List +from parchmint import Target from pymint.mintdevice import MINTDevice from pymint.mintnode import MINTNode -from pymint.minttarget import MINTTarget - - -class MarsStrategy: - def __init__( - self, construction_graph: ConstructionGraph, fig: FluidInteractionGraph - ) -> None: - self._construction_graph: ConstructionGraph = construction_graph - self._fig: FluidInteractionGraph = fig - self._fig_netlist_map: Dict[str, str] = dict() - - def reduce_mapping_options(self) -> None: - # Dummy strategy - for fignode_id in self._fig.nodes: - fignode = self._fig.get_fignode(fignode_id) - - if ConstructionNode(fignode_id).is_explictly_mapped: - pass - else: - if isinstance(fignode, Interaction): - cn = self._construction_graph.get_fignode_cn(fignode) - - del cn.mapping_options[1 : len(cn.mapping_options)] - - # for cn in self._construction_graph.construction_nodes: - # # print(len(cn.mapping_options)) - # # clean this - # # Remove the extra mappings - # print( - # "Reducing mapping options for Construction node: {} from {} to {}".format( - # cn.id, len(cn.mapping_options), 1 - # ), - # ) - # if len(cn.mapping_options) > 1: - # for option in cn.mapping_options: - # print(" -{}".format(option.primitive.mint)) - # del cn.mapping_options[1 : len(cn.mapping_options)] - # # print("... -> {}".format(len(cn.mapping_options))) - - # print("Printing all final mapping options:") - # for cn in self._construction_graph.construction_nodes: - # print("Construction node: {}".format(cn.id)) - # print("Options: ") - - # for mapping_option in cn.mapping_options: - # print(mapping_option.primitive.mint) - - def generate_flow_network(self, fig_subgraph_view) -> MINTDevice: - # TODO - For now just assume that the networks basically are a bunch - # of nodes with nets/channels connecting them - ret = MINTDevice("flow_network_temp") - mint_layer = ret.create_mint_layer("0", "0", 0, MINTLayerType.FLOW) - for node in fig_subgraph_view.nodes: - n = MINTNode("node_{}".format(node), mint_layer) - ret.add_component(n) - self._store_fig_netlist_name(node, n.ID) - i = 1 - for node in fig_subgraph_view.nodes: - # Create the channel between these nodes - channel_name = "c_{}".format(i) - i += 1 - params = dict() - params["channelWidth"] = 400 - source = MINTTarget("node_{}".format(node)) - sinks = [] - - # Add all the outgoing edges - for edge in fig_subgraph_view.out_edges(node): - tar = edge[1] - if tar not in fig_subgraph_view.nodes: - # We skip because this might be a weird edge that we were not supposed - # to have in this network - continue - - sinks.append(MINTTarget("node_{}".format(tar))) - - ret.create_mint_connection( - channel_name, "CHANNEL", params, source, sinks, "0" - ) - - return ret - - def _store_fig_netlist_name(self, fig_id: str, netlist_id: str) -> None: - self._fig_netlist_map[fig_id] = netlist_id - - def _get_fig_netlist_name(self, fig_id: str) -> str: - return self._fig_netlist_map[fig_id] - - def generate_input_connectingoptions(self, subgraph_view) -> List[ConnectingOption]: - subgraph_inputs = [] - for node in list(subgraph_view.nodes): - if len(subgraph_view.in_edges(node)) == 0: - subgraph_inputs.append( - ConnectingOption(self._get_fig_netlist_name(node)) - ) - - return subgraph_inputs - - def generate_output_connectingoptions( - self, subgraph_view - ) -> List[ConnectingOption]: - subgraph_outputs = [] - for node in list(subgraph_view.nodes): - if len(subgraph_view.out_edges(node)) == 0: - subgraph_outputs.append( - ConnectingOption(self._get_fig_netlist_name(node)) - ) - - return subgraph_outputs - - def generate_carrier_connectingoptions( - self, subgraph_view - ) -> List[ConnectingOption]: - return [] +from lfr.netlistgenerator.connectingoption import ConnectingOption - def generate_loading_connectingoptions( - self, subgraph_view - ) -> List[ConnectingOption]: - return [] - def size_netlist(self, device: MINTDevice) -> None: - """ - Sizes the device based on either lookup tables, inverse design algorithms, etc. - """ - dafd_adapter = DAFDAdapter(device) - # Default size for PORT is 2000 um - for component in device.components: - constraints = self._construction_graph.get_component_cn( - component - ).constraints - if component.entity == "NOZZLE DROPLET GENERATOR": - # dafd_adapter.size_droplet_generator(component, constraints) - print("Skipping calling DAFD since its crashing everything right now") - elif component.entity == "PORT": - component.params.set_param("portRadius", 2000) +class MarsStrategy(GenStrategy): + pass diff --git a/lfr/netlistgenerator/generator.py b/lfr/netlistgenerator/generator.py index fc569b7..e339938 100644 --- a/lfr/netlistgenerator/generator.py +++ b/lfr/netlistgenerator/generator.py @@ -1,1489 +1,316 @@ -import sys -from copy import deepcopy -from typing import List, Set -import networkx as nx +from typing import Dict, FrozenSet, List, Optional, Set, Tuple from pymint.mintdevice import MINTDevice -from lfr.graphmatch.interface import get_fig_matches +from pymint.mintlayer import MINTLayerType -from lfr.netlistgenerator.procedural_component_algorithms.ytree import YTREE +from lfr.compiler.module import Module +from lfr.fig.simplification import remove_passthrough_nodes +from lfr.graphmatch.interface import get_fig_matches +from lfr.netlistgenerator import LibraryPrimitivesEntry +from lfr.netlistgenerator.constructiongraph.edge_generation import ( + generate_construction_graph_edges, +) +from lfr.netlistgenerator.constructiongraph.variant_generator import ( + generate_match_variants, +) +from lfr.netlistgenerator.flownetworkmatching import add_flow_flow_matching_candidates from lfr.netlistgenerator.gen_strategies.dropxstrategy import DropXStrategy +from lfr.netlistgenerator.gen_strategies.dummy import DummyStrategy from lfr.netlistgenerator.gen_strategies.marsstrategy import MarsStrategy -from lfr.fig.fluidinteractiongraph import FluidInteractionGraph -from lfr.postprocessor.mapping import NetworkMapping, NodeMappingTemplate -from pymint.mintlayer import MINTLayerType -from lfr.netlistgenerator.primitive import NetworkPrimitive, Primitive, PrimitiveType -from lfr.netlistgenerator.connectingoption import ConnectingOption from lfr.netlistgenerator.mappinglibrary import MappingLibrary -from lfr.netlistgenerator.networkmappingoption import ( - NetworkMappingOption, - NetworkMappingOptionType, -) -from lfr.netlistgenerator.gen_strategies.genstrategy import GenStrategy -from lfr.fig.fignode import IOType, Pump, Storage, ValueNode from lfr.netlistgenerator.namegenerator import NameGenerator -from lfr.netlistgenerator.gen_strategies.dummy import DummyStrategy -from lfr.netlistgenerator.constructionnode import ConstructionNode -from lfr.netlistgenerator.constructiongraph import ConstructionGraph -from lfr.fig.interaction import ( - FluidIntegerInteraction, - FluidNumberInteraction, - InteractionType, +from lfr.netlistgenerator.netlist_generation import generate_device +from lfr.postprocessor.mapping import ( + FluidicOperatorMapping, + NetworkMapping, + NodeMappingInstance, + NodeMappingTemplate, + PumpMapping, + StorageMapping, ) -from lfr.netlistgenerator.mappingoption import MappingOption -from lfr.compiler.module import Module - - -# def generate_MARS_library() -> MappingLibrary: -# # TODO - Programatically create each of the items necessary for the MARS -# primitive library, -# # we shall serialize them after experimentation - -# # mix_primitive = MappingOption() - - -# # mix_primitive.init_single_component(mint_component) - -# # mix_primitive.add -# pass - - -def generate_mlsi_library() -> MappingLibrary: - - library = MappingLibrary("mlsi") - # PORT - port_inputs = [] - port_inputs.append(ConnectingOption(None, [None])) - port_inputs.append(ConnectingOption(None, [None])) - port_inputs.append(ConnectingOption(None, [None])) - port_inputs.append(ConnectingOption(None, [None])) - port_inputs.append(ConnectingOption(None, [None])) - port_inputs.append(ConnectingOption(None, [None])) - port_inputs.append(ConnectingOption(None, [None])) - port_inputs.append(ConnectingOption(None, [None])) - port_inputs.append(ConnectingOption(None, [None])) - port_inputs.append(ConnectingOption(None, [None])) - port_inputs.append(ConnectingOption(None, [None])) - port_inputs.append(ConnectingOption(None, [None])) - port_inputs.append(ConnectingOption(None, [None])) - port_inputs.append(ConnectingOption(None, [None])) - port_inputs.append(ConnectingOption(None, [None])) - port_inputs.append(ConnectingOption(None, [None])) - port_inputs.append(ConnectingOption(None, [None])) - port_inputs.append(ConnectingOption(None, [None])) - port_inputs.append(ConnectingOption(None, [None])) - - port_outputs = [] - port_outputs.append(ConnectingOption(None, [])) - port_outputs.append(ConnectingOption(None, [])) - port_outputs.append(ConnectingOption(None, [])) - port_outputs.append(ConnectingOption(None, [])) - port_outputs.append(ConnectingOption(None, [])) - port_outputs.append(ConnectingOption(None, [])) - port_outputs.append(ConnectingOption(None, [])) - port_outputs.append(ConnectingOption(None, [])) - port_outputs.append(ConnectingOption(None, [])) - port_outputs.append(ConnectingOption(None, [])) - port_outputs.append(ConnectingOption(None, [])) - port_outputs.append(ConnectingOption(None, [])) - port_outputs.append(ConnectingOption(None, [])) - port_outputs.append(ConnectingOption(None, [])) - port_outputs.append(ConnectingOption(None, [])) - port_outputs.append(ConnectingOption(None, [])) - port_outputs.append(ConnectingOption(None, [])) - port_outputs.append(ConnectingOption(None, [])) - port_outputs.append(ConnectingOption(None, [])) - - port = Primitive( - "PORT", - PrimitiveType.COMPONENT, - r"""{ - v1:IO - }""", - False, - False, - port_inputs, - port_outputs, - None, - None, - None, - ) - - library.add_io_entry(port) - - # MIXER - CONTINOUS FLOW ONE - - cf_mixer_inputs = [] - - cf_mixer_inputs.append(ConnectingOption(None, ["1"])) - cf_mixer_inputs.append(ConnectingOption(None, ["1"])) - cf_mixer_inputs.append(ConnectingOption(None, ["1"])) - cf_mixer_inputs.append(ConnectingOption(None, ["1"])) - cf_mixer_inputs.append(ConnectingOption(None, ["1"])) - cf_mixer_inputs.append(ConnectingOption(None, ["1"])) - cf_mixer_inputs.append(ConnectingOption(None, ["1"])) - cf_mixer_inputs.append(ConnectingOption(None, ["1"])) - cf_mixer_inputs.append(ConnectingOption(None, ["1"])) - cf_mixer_inputs.append(ConnectingOption(None, ["1"])) - - cf_mixer_outputs = [] - - cf_mixer_outputs.append(ConnectingOption(None, ["2"])) - - cf_mixer_loadings = [] - cf_mixer_carriers = [] - - cf_mixer = Primitive( - "MIXER", - PrimitiveType.COMPONENT, - r"""{ - v1:MIX - }""", - False, - False, - cf_mixer_inputs, - cf_mixer_outputs, - cf_mixer_loadings, - cf_mixer_carriers, - None, - ) - - library.add_operator_entry(cf_mixer, InteractionType.MIX) - - # MUX2 - - mux2_inputs = [] - mux2_inputs.append(ConnectingOption(None, ["1"])) - - mux2_outputs = [] - mux2_outputs.append(ConnectingOption(None, ["2"])) - mux2_outputs.append(ConnectingOption(None, ["3"])) - - mux2 = Primitive( - "MUX", - PrimitiveType.COMPONENT, - r"""{ - v1 { "DISTRIBUTE_OR", "or_1" }, - v1 -> vo1 { "DISTRIBUTE_OR", "or_1" }, - v1 -> vo2 { "DISTRIBUTE_OR", "or_1" } - } - """, - False, - False, - mux2_inputs, - mux2_outputs, - None, - None, - None, - ) - - library.add_entry(mux2) - - return library - - -def generate_mars_library() -> MappingLibrary: - - library = MappingLibrary("mars") - - # PORT - port_inputs = [] - port_inputs.append(ConnectingOption(None, [None])) - port_inputs.append(ConnectingOption(None, [None])) - port_inputs.append(ConnectingOption(None, [None])) - port_inputs.append(ConnectingOption(None, [None])) - port_inputs.append(ConnectingOption(None, [None])) - port_inputs.append(ConnectingOption(None, [None])) - port_inputs.append(ConnectingOption(None, [None])) - port_inputs.append(ConnectingOption(None, [None])) - port_inputs.append(ConnectingOption(None, [None])) - port_inputs.append(ConnectingOption(None, [None])) - port_inputs.append(ConnectingOption(None, [None])) - port_inputs.append(ConnectingOption(None, [None])) - port_inputs.append(ConnectingOption(None, [None])) - port_inputs.append(ConnectingOption(None, [None])) - port_inputs.append(ConnectingOption(None, [None])) - port_inputs.append(ConnectingOption(None, [None])) - port_inputs.append(ConnectingOption(None, [None])) - port_inputs.append(ConnectingOption(None, [None])) - port_inputs.append(ConnectingOption(None, [None])) - - port_outputs = [] - port_outputs.append(ConnectingOption(None, [])) - port_outputs.append(ConnectingOption(None, [])) - port_outputs.append(ConnectingOption(None, [])) - port_outputs.append(ConnectingOption(None, [])) - port_outputs.append(ConnectingOption(None, [])) - port_outputs.append(ConnectingOption(None, [])) - port_outputs.append(ConnectingOption(None, [])) - port_outputs.append(ConnectingOption(None, [])) - port_outputs.append(ConnectingOption(None, [])) - port_outputs.append(ConnectingOption(None, [])) - port_outputs.append(ConnectingOption(None, [])) - port_outputs.append(ConnectingOption(None, [])) - port_outputs.append(ConnectingOption(None, [])) - port_outputs.append(ConnectingOption(None, [])) - port_outputs.append(ConnectingOption(None, [])) - port_outputs.append(ConnectingOption(None, [])) - port_outputs.append(ConnectingOption(None, [])) - port_outputs.append(ConnectingOption(None, [])) - port_outputs.append(ConnectingOption(None, [])) - - port = Primitive( - "PORT", - PrimitiveType.COMPONENT, - "IO", - False, - False, - port_inputs, - port_outputs, - None, - None, - None, - ) - - library.add_io_entry(port) - - # NORMAL MIXER - - mixer_inputs = [] - - mixer_inputs.append(ConnectingOption(None, ["1"])) - mixer_inputs.append(ConnectingOption(None, ["1"])) - mixer_inputs.append(ConnectingOption(None, ["1"])) - mixer_inputs.append(ConnectingOption(None, ["1"])) - mixer_inputs.append(ConnectingOption(None, ["1"])) - mixer_inputs.append(ConnectingOption(None, ["1"])) - mixer_inputs.append(ConnectingOption(None, ["1"])) - mixer_inputs.append(ConnectingOption(None, ["1"])) - mixer_inputs.append(ConnectingOption(None, ["1"])) - mixer_inputs.append(ConnectingOption(None, ["1"])) - - mixer_outputs = [] - - mixer_outputs.append(ConnectingOption(None, ["2"])) - mixer_outputs.append(ConnectingOption(None, ["2"])) - mixer_outputs.append(ConnectingOption(None, ["2"])) - mixer_outputs.append(ConnectingOption(None, ["2"])) - mixer_outputs.append(ConnectingOption(None, ["2"])) - mixer_outputs.append(ConnectingOption(None, ["2"])) - mixer_outputs.append(ConnectingOption(None, ["2"])) - mixer_outputs.append(ConnectingOption(None, ["2"])) - mixer_outputs.append(ConnectingOption(None, ["2"])) - mixer_outputs.append(ConnectingOption(None, ["2"])) - - mixer_loadings = [] - mixer_carriers = [] - - mixer = Primitive( - "MIXER", - PrimitiveType.COMPONENT, - "MIX", - False, - False, - mixer_inputs, - mixer_outputs, - mixer_loadings, - mixer_carriers, - None, - ) - - library.add_operator_entry(mixer, InteractionType.MIX) - - # DIAMOND CHAMBER - - diamond_chamber_inputs = [] - - diamond_chamber_inputs.append(ConnectingOption("default_component", ["1"])) - - diamond_chamber_outputs = [] - - diamond_chamber_outputs.append(ConnectingOption("default_component", ["2"])) - - diamond_chamber_loadings = [] - diamond_chamber_carriers = [] - - diamond_chamber = Primitive( - "DIAMOND CHAMBER", - PrimitiveType.COMPONENT, - "PROCESS", - False, - False, - diamond_chamber_inputs, - diamond_chamber_outputs, - diamond_chamber_loadings, - diamond_chamber_carriers, - None, - ) - - library.add_operator_entry(diamond_chamber, InteractionType.TECHNOLOGY_PROCESS) - - # METER - - meter_inputs = [] - - meter_outputs = [] - - meter_outputs.append(ConnectingOption("default_component", ["1"])) - - meter_loadings = [] - - meter_loadings.append(ConnectingOption("default_component", ["2"])) - - meter_carriers = [] - - meter_carriers.append(ConnectingOption("default_component", ["3"])) - - meter = Primitive( - "METER", - PrimitiveType.NETLIST, - "METER", - False, - False, - meter_inputs, - meter_outputs, - meter_loadings, - meter_carriers, - "default-netlists/dropletgenerator.mint", - ["droplet_size", "generation_rate"], - [ - "orifice_size", - "aspect_ratio", - "capillary_number", - "expansion_ratio", - "flow_rate_ratio", - "normalized_oil_inlet", - "normalized_orifice_length", - "normalized_water_inlet", - ], - ) - - library.add_operator_entry(meter, InteractionType.METER) - - # Incubator - - incubator_inputs = [] - - incubator_inputs.append(ConnectingOption("default_component", ["1"])) - - incubator_outputs = [] - - incubator_outputs.append(ConnectingOption("default_component", ["1"])) - - incubator_loadings = [] - incubator_carriers = [] - - incubator = Primitive( - "INCUBATOR", - PrimitiveType.COMPONENT, - "PROCESS", - False, - False, - incubator_inputs, - incubator_outputs, - incubator_loadings, - incubator_carriers, - ) - - library.add_operator_entry(incubator, InteractionType.TECHNOLOGY_PROCESS) - - # SORTER - - sorter_inputs = [] - - sorter_inputs.append(ConnectingOption(None, ["1"])) - - sorter_outputs = [] - - sorter_outputs.append(ConnectingOption(None, ["2"])) - sorter_outputs.append(ConnectingOption(None, ["3"])) - - sorter_loadings = [] - sorter_carriers = [] - - # TODO - Modify this later on - sorter = Primitive( - "DROPLET SORTER", - PrimitiveType.COMPONENT, - "SIEVE", - False, - False, - sorter_inputs, - sorter_outputs, - sorter_loadings, - sorter_carriers, - None, - ) - - library.add_operator_entry(sorter, InteractionType.SIEVE) - - return library - - -def generate_dropx_library() -> MappingLibrary: - - library = MappingLibrary("dropx") - - # PORT - port_inputs = [] - port_inputs.append(ConnectingOption(None, [None])) - port_inputs.append(ConnectingOption(None, [None])) - port_inputs.append(ConnectingOption(None, [None])) - port_inputs.append(ConnectingOption(None, [None])) - port_inputs.append(ConnectingOption(None, [None])) - port_inputs.append(ConnectingOption(None, [None])) - port_inputs.append(ConnectingOption(None, [None])) - port_inputs.append(ConnectingOption(None, [None])) - port_inputs.append(ConnectingOption(None, [None])) - port_inputs.append(ConnectingOption(None, [None])) - port_inputs.append(ConnectingOption(None, [None])) - port_inputs.append(ConnectingOption(None, [None])) - port_inputs.append(ConnectingOption(None, [None])) - port_inputs.append(ConnectingOption(None, [None])) - port_inputs.append(ConnectingOption(None, [None])) - port_inputs.append(ConnectingOption(None, [None])) - port_inputs.append(ConnectingOption(None, [None])) - port_inputs.append(ConnectingOption(None, [None])) - port_inputs.append(ConnectingOption(None, [None])) - - port_outputs = [] - port_outputs.append(ConnectingOption(None, [])) - port_outputs.append(ConnectingOption(None, [])) - port_outputs.append(ConnectingOption(None, [])) - port_outputs.append(ConnectingOption(None, [])) - port_outputs.append(ConnectingOption(None, [])) - port_outputs.append(ConnectingOption(None, [])) - port_outputs.append(ConnectingOption(None, [])) - port_outputs.append(ConnectingOption(None, [])) - port_outputs.append(ConnectingOption(None, [])) - port_outputs.append(ConnectingOption(None, [])) - port_outputs.append(ConnectingOption(None, [])) - port_outputs.append(ConnectingOption(None, [])) - port_outputs.append(ConnectingOption(None, [])) - port_outputs.append(ConnectingOption(None, [])) - port_outputs.append(ConnectingOption(None, [])) - port_outputs.append(ConnectingOption(None, [])) - port_outputs.append(ConnectingOption(None, [])) - port_outputs.append(ConnectingOption(None, [])) - port_outputs.append(ConnectingOption(None, [])) - - port = Primitive( - "PORT", - PrimitiveType.COMPONENT, - r"""{ - v1:IO - }""", - False, - False, - port_inputs, - port_outputs, - None, - None, - None, - ) - - library.add_io_entry(port) - - # PICO INJECTOR - - pico_injector_inputs = [] - - pico_injector_inputs.append(ConnectingOption(None, ["1"])) - pico_injector_inputs.append(ConnectingOption(None, ["2"])) - - pico_injector_outputs = [] - - pico_injector_outputs.append(ConnectingOption(None, ["3"])) - - pico_injector_loadings = [] - pico_injector_carriers = [] - - pico_injector = Primitive( - "PICOINJECTOR", - PrimitiveType.COMPONENT, - r"""{ - v1:MIX - }""", - False, - False, - pico_injector_inputs, - pico_injector_outputs, - pico_injector_loadings, - pico_injector_carriers, - None, - ) - - library.add_operator_entry(pico_injector, InteractionType.MIX) - - # DROPLET ELECTROPHORESIS MERGER - - electrophoresis_merger_inputs = [] - - electrophoresis_merger_inputs.append(ConnectingOption(None, ["1"])) - electrophoresis_merger_inputs.append(ConnectingOption(None, ["2"])) - - electrophoresis_merger_outputs = [] - - electrophoresis_merger_outputs.append(ConnectingOption(None, ["3"])) - - electrophoresis_merger_loadings = [] - electrophoresis_merger_carriers = [] - - # TODO - Modify this later on - electrophoresis_merger = Primitive( - "DROPLET MERGER", - PrimitiveType.COMPONENT, - r"""{ - v1:MIX - }""", - False, - False, - electrophoresis_merger_inputs, - electrophoresis_merger_outputs, - electrophoresis_merger_loadings, - electrophoresis_merger_carriers, - None, - ) - - library.add_operator_entry(electrophoresis_merger, InteractionType.MIX) - - # DROPLET SORTER - - droplet_sorter_inputs = [] - - droplet_sorter_inputs.append(ConnectingOption(None, ["1"])) - - droplet_sorter_outputs = [] - - droplet_sorter_outputs.append(ConnectingOption(None, ["2"])) - droplet_sorter_outputs.append(ConnectingOption(None, ["3"])) - - droplet_sorter_loadings = [] - droplet_sorter_carriers = [] - - # TODO - Modify this later on - droplet_sorter = Primitive( - "DROPLET SORTER", - PrimitiveType.COMPONENT, - r"""{ - v1:SIEVE - }""", - False, - False, - droplet_sorter_inputs, - droplet_sorter_outputs, - droplet_sorter_loadings, - droplet_sorter_carriers, - None, - ) - - library.add_operator_entry(droplet_sorter, InteractionType.SIEVE) - - # DROPLET GENERATOR - - droplet_generator_inputs = [] - - droplet_generator_inputs.append(ConnectingOption("default_component", ["1"])) - - droplet_generator_outputs = [] - - droplet_generator_outputs.append(ConnectingOption("default_component", ["3"])) - - droplet_generator_loadings = [] - droplet_generator_carriers = [] - - droplet_generator = Primitive( - "NOZZLE DROPLET GENERATOR", - PrimitiveType.NETLIST, - r"""{ - v1:METER - }""", - False, - False, - droplet_generator_inputs, - droplet_generator_outputs, - droplet_generator_loadings, - droplet_generator_carriers, - "default-netlists/dropletgenerator.mint", - ["droplet_size", "generation_rate"], - [ - "orifice_size", - "aspect_ratio", - "capillary_number", - "expansion_ratio", - "flow_rate_ratio", - "normalized_oil_inlet", - "normalized_orifice_length", - "normalized_water_inlet", - ], - ) - - library.add_operator_entry(droplet_generator, InteractionType.METER) - - droplet_merger_junction_inputs = [] - - droplet_merger_junction_inputs.append(ConnectingOption(None, ["1"])) - droplet_merger_junction_inputs.append(ConnectingOption(None, ["2"])) - - droplet_merger_junction_outputs = [] - - droplet_merger_junction_outputs.append(ConnectingOption(None, ["3"])) - - droplet_merger_junction_loadings = [] - droplet_merger_junction_carriers = [] - - droplet_merger_junction = Primitive( - "DROPLET MERGER JUNCTION", - PrimitiveType.COMPONENT, - r"""{ - v1:MIX - }""", - False, - False, - droplet_merger_junction_inputs, - droplet_merger_junction_outputs, - droplet_merger_junction_loadings, - droplet_merger_junction_carriers, - None, - ) - - library.add_operator_entry(droplet_merger_junction, InteractionType.MIX) - - # DROPLET MERGER CHANNEL - - droplet_merger_channel_inputs = [] - - droplet_merger_channel_inputs.append(ConnectingOption(None, ["1"])) - - droplet_merger_channel_outputs = [] - - droplet_merger_channel_outputs.append(ConnectingOption(None, ["2"])) - - droplet_merger_channel_loadings = [] - droplet_merger_channel_carriers = [] - - droplet_merger_channel = Primitive( - "DROPLET MERGER CHANNEL", - PrimitiveType.COMPONENT, - r"""{ - v1:MIX - }""", - False, - False, - droplet_merger_channel_inputs, - droplet_merger_channel_outputs, - droplet_merger_channel_loadings, - droplet_merger_channel_carriers, - None, - ) - - library.add_operator_entry(droplet_merger_channel, InteractionType.MIX) - - # MIXER - CONTINOUS FLOW ONE - - cf_mixer_inputs = [] - - cf_mixer_inputs.append(ConnectingOption(None, ["1"])) - cf_mixer_inputs.append(ConnectingOption(None, ["1"])) - cf_mixer_inputs.append(ConnectingOption(None, ["1"])) - cf_mixer_inputs.append(ConnectingOption(None, ["1"])) - cf_mixer_inputs.append(ConnectingOption(None, ["1"])) - cf_mixer_inputs.append(ConnectingOption(None, ["1"])) - cf_mixer_inputs.append(ConnectingOption(None, ["1"])) - cf_mixer_inputs.append(ConnectingOption(None, ["1"])) - cf_mixer_inputs.append(ConnectingOption(None, ["1"])) - cf_mixer_inputs.append(ConnectingOption(None, ["1"])) - - cf_mixer_outputs = [] - - cf_mixer_outputs.append(ConnectingOption(None, ["2"])) - - cf_mixer_loadings = [] - cf_mixer_carriers = [] - - cf_mixer = Primitive( - "MIXER", - PrimitiveType.COMPONENT, - r"""{ - v1:MIX - }""", - False, - False, - cf_mixer_inputs, - cf_mixer_outputs, - cf_mixer_loadings, - cf_mixer_carriers, - None, - ) - - library.add_operator_entry(cf_mixer, InteractionType.MIX) - - # DROPLET SPLITTER - - droplet_splitter_inputs = [] - - droplet_splitter_inputs.append(ConnectingOption(None, ["1"])) - - droplet_splitter_outputs = [] - - droplet_splitter_outputs.append(ConnectingOption(None, ["2"])) - droplet_splitter_outputs.append(ConnectingOption(None, ["3"])) - - droplet_splitter_loadings = [] - droplet_splitter_carriers = [] - - droplet_splitter = Primitive( - "DROPLET SPLITTER", - PrimitiveType.COMPONENT, - r"""{ - v1:DIVIDE - }""", - False, - False, - droplet_splitter_inputs, - droplet_splitter_outputs, - droplet_splitter_loadings, - droplet_splitter_carriers, - None, - ) - - library.add_operator_entry(droplet_splitter, InteractionType.DIVIDE) - - # NORMAL MIXER - - mixer_inputs = [] - - mixer_inputs.append(ConnectingOption(None, ["1"])) - mixer_inputs.append(ConnectingOption(None, ["1"])) - mixer_inputs.append(ConnectingOption(None, ["1"])) - mixer_inputs.append(ConnectingOption(None, ["1"])) - mixer_inputs.append(ConnectingOption(None, ["1"])) - mixer_inputs.append(ConnectingOption(None, ["1"])) - mixer_inputs.append(ConnectingOption(None, ["1"])) - mixer_inputs.append(ConnectingOption(None, ["1"])) - mixer_inputs.append(ConnectingOption(None, ["1"])) - mixer_inputs.append(ConnectingOption(None, ["1"])) - - mixer_outputs = [] - - mixer_outputs.append(ConnectingOption(None, ["2"])) - mixer_outputs.append(ConnectingOption(None, ["2"])) - mixer_outputs.append(ConnectingOption(None, ["2"])) - mixer_outputs.append(ConnectingOption(None, ["2"])) - mixer_outputs.append(ConnectingOption(None, ["2"])) - mixer_outputs.append(ConnectingOption(None, ["2"])) - mixer_outputs.append(ConnectingOption(None, ["2"])) - mixer_outputs.append(ConnectingOption(None, ["2"])) - mixer_outputs.append(ConnectingOption(None, ["2"])) - mixer_outputs.append(ConnectingOption(None, ["2"])) - - mixer_loadings = [] - mixer_carriers = [] - - mixer = Primitive( - "MIXER", - PrimitiveType.COMPONENT, - r"""{ - v1:MIX - }""", - False, - False, - mixer_inputs, - mixer_outputs, - mixer_loadings, - mixer_carriers, - None, - ) - - library.add_operator_entry(mixer, InteractionType.MIX) - - # DROPLET CAPACITANCE SENSOR - - droplet_capacitance_sensor_inputs = [] - - droplet_capacitance_sensor_inputs.append(ConnectingOption(None, ["1"])) - - droplet_capacitance_sensor_outputs = [] - - droplet_capacitance_sensor_outputs.append(ConnectingOption(None, ["2"])) - - droplet_capacitance_sensor_loadings = [] - droplet_capacitance_sensor_carriers = [] - - droplet_capacitance_sensor = Primitive( - "DROPLET CAPACITANCE SENSOR", - PrimitiveType.COMPONENT, - r"""{ - v1:PROCESS - }""", - False, - False, - droplet_capacitance_sensor_inputs, - droplet_capacitance_sensor_outputs, - droplet_capacitance_sensor_loadings, - droplet_capacitance_sensor_carriers, - None, - ) - - library.add_operator_entry( - droplet_capacitance_sensor, InteractionType.TECHNOLOGY_PROCESS - ) - - # FILTER - - filter_inputs = [] - - filter_inputs.append(ConnectingOption(None, ["1"])) - - filter_outputs = [] - - filter_outputs.append(ConnectingOption(None, ["2"])) - - filter_loadings = [] - filter_carriers = [] - - filter = Primitive( - "FILTER", - PrimitiveType.COMPONENT, - r"""{ - v1:PROCESS - }""", - False, - False, - filter_inputs, - filter_outputs, - filter_loadings, - filter_carriers, - None, - ) - - library.add_operator_entry(filter, InteractionType.TECHNOLOGY_PROCESS) - - # DROPLET FLUORESCENCE SENSOR - - droplet_fluorescence_sensor_inputs = [] - - droplet_fluorescence_sensor_inputs.append(ConnectingOption(None, ["1"])) - - droplet_fluorescence_sensor_outputs = [] - - droplet_fluorescence_sensor_outputs.append(ConnectingOption(None, ["2"])) - - droplet_fluorescence_sensor_loadings = [] - droplet_fluorescence_sensor_carriers = [] - - droplet_fluorescence_sensor = Primitive( - "DROPLET FLUORESCENCE SENSOR", - PrimitiveType.COMPONENT, - r"""{ - v1:PROCESS - }""", - False, - False, - droplet_fluorescence_sensor_inputs, - droplet_fluorescence_sensor_outputs, - droplet_fluorescence_sensor_loadings, - droplet_fluorescence_sensor_carriers, - None, - ) - - library.add_operator_entry( - droplet_fluorescence_sensor, InteractionType.TECHNOLOGY_PROCESS - ) - - # DROPLET LUMINESCENCE SENSOR - droplet_luminescence_sensor_inputs = [] - - droplet_luminescence_sensor_inputs.append(ConnectingOption(None, ["1"])) - - droplet_luminescence_sensor_outputs = [] - - droplet_luminescence_sensor_outputs.append(ConnectingOption(None, ["2"])) - - droplet_luminescence_sensor_loadings = [] - droplet_luminescence_sensor_carriers = [] - - droplet_luminescence_sensor = Primitive( - "DROPLET LUMINESCENCE SENSOR", - PrimitiveType.COMPONENT, - r"""{ - v1:PROCESS - }""", - False, - False, - droplet_luminescence_sensor_inputs, - droplet_luminescence_sensor_outputs, - droplet_luminescence_sensor_loadings, - droplet_luminescence_sensor_carriers, - None, - ) - - library.add_operator_entry( - droplet_luminescence_sensor, InteractionType.TECHNOLOGY_PROCESS - ) - - # DROPLET SPACER - - droplet_spacer_inputs = [] - - droplet_spacer_inputs.append(ConnectingOption("default_component", ["1"])) - - droplet_spacer_outputs = [] - - droplet_spacer_outputs.append(ConnectingOption("default_component", ["2"])) - - droplet_spacer_loadings = [] - droplet_spacer_carriers = [] - - droplet_spacer = Primitive( - "DROPLET SPACER", - PrimitiveType.NETLIST, - r"""{ - v1:PROCESS - }""", - False, - False, - droplet_spacer_inputs, - droplet_spacer_outputs, - droplet_spacer_loadings, - droplet_spacer_carriers, - "default-netlists/dropletspacer.mint", - ) - - library.add_operator_entry(droplet_spacer, InteractionType.TECHNOLOGY_PROCESS) - - # YTREE - This is a procedural primitives - - ytree = YTREE() - - library.add_procedural_entry(ytree) - - return library - - -def generate(module: Module, library: MappingLibrary) -> MINTDevice: - - construction_graph = ConstructionGraph() - - name_generator = NameGenerator() - - cur_device = MINTDevice(module.name) - - # Add a MINT Layer so that the device has something to work with - cur_device.create_mint_layer("0", "0", 0, MINTLayerType.FLOW) - +from lfr.utils import printgraph + + +def generate(module: Module, library: MappingLibrary) -> List[MINTDevice]: + # In order to create the device, we do the following + # STEP 1 - Simplify the Fluid Interaction Graphs + # STEP 2 - Initialize the active strategy + # STEP 3 - Get all the technology mapping matches for the FIG + # STEP 4 - Eliminate the matches that are exactly the same as the explicit matches + # STEP 5 - Generate the waste outputs + # STEP 6 - Generate the mapping variants + # STEP 6.5 - Generate the flow subgraph matches (TODO - Add test cases for this) + # STEP 6.10 - Before generating teh device, delete all the variants with incomplete mappings + # STEP 7 - Generate the control logic network + # STEP 8 - Generate the connections + # STEP 9 - Size the components + # STEP 10 - Size the connections + + # construction_graph = ConstructionGraph() + + # Step 1 - Simplify the Fluid Interaction Graphs + printgraph(module.FIG, f"{module.name}_FIG") + remove_passthrough_nodes(module.FIG) + printgraph(module.FIG, f"{module.name}_FIG_simplified") + + # STEP 2 - Initialize the active strategy # TODO - I need to change this DummyStrategy later on if library.name == "dropx": - active_strategy = DropXStrategy(construction_graph, module.FIG) + active_strategy = DropXStrategy(module.FIG) elif library.name == "mars": # raise NotImplementedError() - active_strategy = MarsStrategy(construction_graph, module.FIG) + active_strategy = MarsStrategy(module.FIG) elif library.name == "hmlp": raise NotImplementedError() else: - active_strategy = DummyStrategy(construction_graph, module.FIG) - - # First go through all the interactions in the design + active_strategy = DummyStrategy(module.FIG) - # IF interaction is mix/sieve/divide/dilute/meter look at the library - # to get all the options available and set them up as options for the - # construction graph to pick and choose from the options. - # - # FUTURE WORK - # + # STEP 3 - Get all the technology mapping matches for the FIG # Do the reggie matching to find the mapping options # This means that we might need to have a forest of construction of graphs # as there would be alternatives for each type of mapping matches = get_fig_matches(module.FIG, library) + print(f"Total Matches against library : {len(matches)}") for match in matches: + # Generate an object that is usable going forward (mapping template perhaps) print(match) - # Map the interactions in the fig to individual library options - for interaction in module.FIG.get_interactions(): - operator_candidates = library.get_operators(interaction_type=interaction.type) - cn = ConstructionNode(interaction.ID) - # if isinstance(interaction, ValueNode): - # continue - - for operator_candidate in operator_candidates: - # TODO: This will change in the future when we can match subgraphs correctly - if isinstance( - interaction, (FluidNumberInteraction, FluidIntegerInteraction) - ): - # Basically add the value node id into the subgraph view also - node_ids = [ - module.FIG.get_fignode(edge[0]).ID - for edge in module.FIG.in_edges(interaction.ID) - if isinstance(module.FIG.get_fignode(edge[0]), ValueNode) - ] - node_ids.append(interaction.ID) - sub_graph = module.FIG.subgraph(node_ids) - else: - sub_graph = module.FIG.subgraph(interaction.ID) - mapping_option = MappingOption(operator_candidate, sub_graph) - cn.add_mapping_option(mapping_option) - - construction_graph.add_construction_node(cn) - - # Generate all ports necessary for the Explicitly declared IO - # ------- - # Generate the flow layer IO. These are typically declared explicitly - # TODO - Figure out how we should generate the construction nodes for control - # networks - - for io_ref in module.io: - if io_ref.type is IOType.CONTROL: - continue - for io in io_ref.vector_ref: - cn = ConstructionNode(io.ID) - sub_graph = module.FIG.subgraph(io.ID) - mapping_candidate = library.get_default_IO() - mapping_option = MappingOption(mapping_candidate, sub_graph) - cn.add_mapping_option(mapping_option) - - construction_graph.add_construction_node(cn) + # STEP 4 - Eliminate the matches that are exactly the same as the explicit matches + # Get the explicit mapping and find the explicit mappings here + explicit_mappings = module.get_explicit_mappings() + matches, explict_cover_sets = eliminate_explicit_match_alternates( + matches, explicit_mappings, library + ) - # Map the storage and pump elements to their own individual construction graph nodes - for fig_node_id in list(module.FIG.nodes): - fig_node = module.FIG.get_fignode(fig_node_id) - if isinstance(fig_node, Pump): - cn = ConstructionNode(fig_node.ID) - sub_graph = module.FIG.subgraph(fig_node_id) - mapping_candidates = library.get_pump_entries() - for mapping_candidate in mapping_candidates: - mapping_option = MappingOption(mapping_candidate, sub_graph) - cn.add_mapping_option(mapping_option) + print( + "Total matches against library after explicit mapping eliminations:" + f" {len(matches)}" + ) + for match in matches: + print(match) - elif isinstance(fig_node, Storage): - cn = ConstructionNode(fig_node.ID) - sub_graph = module.FIG.subgraph(fig_node_id) - mapping_candidates = library.get_storage_entries() - for mapping_candidate in mapping_candidates: - mapping_option = MappingOption(mapping_candidate, sub_graph) - cn.add_mapping_option(mapping_option) + # STEP 5 - Generate the waste outputs + # TODO - Add fignodes to all the orphaned flow nodes for this to function + # connect_orphan_IO() - # TODO - Validate if this is a legit way to do things - mappings = module.get_explicit_mappings() - override_network_mappings(mappings, library, module.FIG, construction_graph) + # STEP 6 - Generate the mapping variants + variants = generate_match_variants( + matches, module.FIG, library, active_strategy, explict_cover_sets + ) - # TODO - Go through the different flow-flow edge networks to generate construction - # nodes specific to these networks, Conditions: - # if its a 1-1 flow-flow connection, then create a construction node for the two - # flow nodes - # if its a 1-n / n-1 / n-n construction nodes, then create a construction node - # capturing the whole network + # STEP 6.5 -Generate the matches for the flow subgraphs + add_flow_flow_matching_candidates(module.FIG, variants, active_strategy) - # TODO - Deal with coverage issues here since we need to figure out what are the - # flow networks, that we want to match first and then ensure that they're no - # included on any list - cn_nodes = get_flow_flow_candidates(module, active_strategy) - for cn in cn_nodes: - construction_graph.add_construction_node(cn) + # STEP 8 - Generate the edges in the construction graph + print("Generating the construction graph edges...") + for variant in variants: + generate_construction_graph_edges(module.FIG, variant) + variant.print_graph(f"{variant.ID}_construction_graph.dot") - # Apply all the explicit mappings in the module to the nodes, overwriting - # the options from the library to match against - # TODO - Modify Explicit Mapping Data structure + # STEP 6.10 - Before generating the device, delete all the variants with incomplete coverage of the FIG + variants = [variant for variant in variants if variant.is_fig_fully_covered()] - # Find all the explicit mappings and override them in the construction graph - override_mappings(mappings, library, module.FIG, construction_graph) + # Perform the various validate using the active strategy + validated_variants = [] + for variant in variants: + flow_validation_success = active_strategy.validate_construction_graph_flow( + variant + ) - # Whittle Down the mapping options here to only include the requried single - # candidates - # TODO - Check what library is being used and use the required library here - active_strategy.reduce_mapping_options() + # TODO - Add other kinds of validation here + # Eg. active_strategy.whatever_else_validation() - # TODO - Consider what needs to get done for a combinatorial design space - # ---------------- - # Generate edges in the construction graph, these edges will guide the generation/ - # reduction of path and pipelineing that needs to get done for mars devices - construction_graph.generate_edges(module.FIG) + if flow_validation_success: + validated_variants.append(variant) - # TODO - Extract all pass through networks - eliminate_passthrough_nodes(construction_graph) + # Now generate the devices for each of the variants + generated_devices = [] + for variant in validated_variants: + # Create the device for each of the variants + name_generator = NameGenerator() - # Now since all the mapping options are finalized Extract the netlist necessary - construction_graph.construct_components(name_generator, cur_device) + cur_device = MINTDevice(module.name) - # TODO - Rewrite this whole thing !!! - construction_graph.construct_connections(name_generator, cur_device) + # Add a MINT Layer so that the device has something to work with + cur_device.create_mint_layer("0", "0", 0, MINTLayerType.FLOW) - # Finally join all the netlist pieces attached to the construction nodes - # and the input/output/load/carrier flows - # TODO - MINIMIZE - carrier / load flows - this might require us to generate - # multiple netlist options and pick the best - construction_graph.generate_flow_cn_edges(module) + generate_device( + construction_graph=variant, + scaffhold_device=cur_device, + name_generator=name_generator, + mapping_library=library, + ) + # STEP 8 - Generate the control logic network + # TODO - Whatever this entails (put in the implementation) - construction_graph.generate_control_cn_edges(module) + # STEP 9 - Generate the connection optimizations + # TODO - write the algorithm for carriers and optimize the flows + # Generate all the unaccounted carriers and waste output lines necessary - # Generate all the unaccounted carriers and waste output lines necessary - # for this to function - connect_orphan_IO() + # STEP 10 - Size the components + # TODO - Size the component netlist - # Size the component netlist - active_strategy.size_netlist(cur_device) + generated_devices.append(cur_device) - return cur_device + return generated_devices -def override_mappings( - mappings: List[NodeMappingTemplate], - mapping_library: MappingLibrary, - fig: FluidInteractionGraph, - construction_graph: ConstructionGraph, -) -> None: - # Go through the entire set of mappings in the FIG and generate / append the - # mapping options - # Step 1 - Loop through each of the mappingtemplates - # Step 2 - Loop through each of the instances in teh mappingtemplate - # Step 3 - Find the cn associated with each of the fig nodes and override - # the explicit mapping if mappingtemplate has an associated technology string - for mapping in mappings: - for instance in mapping.instances: +def eliminate_explicit_match_alternates( + matches: List[LibraryPrimitivesEntry], + explict_mappings: List[NodeMappingTemplate], + library: MappingLibrary, +) -> Tuple[List[LibraryPrimitivesEntry], List[Set[str]]]: + """Eliminates the alternatives for explicit matches from the list of matches. - primitive_to_use = None - if mapping.technology_string is not None: - # Create a mapping option from the library with the corresponding info - primitive_to_use = mapping_library.get_primitive( - mapping.technology_string - ) + Args: + matches (List[LibraryPrimitivesEntry]): List of matches to eliminate from + explict_mappings (List[NodeMappingTemplate]): The mappings that are explicitly + defined by the user - node_ids = [] - cn = None # Get the right construction node for doing the stuff - cn_mapping_options = [] + Returns: + List[Tuple[str, Dict[str, str]]]: _description_ + """ + # extract the fignode ID set from matches + match_node_set_dict: Dict[FrozenSet, List[LibraryPrimitivesEntry]] = {} + for match in matches: + frozen_set = frozenset(match[2].keys()) + if frozen_set not in match_node_set_dict: + match_node_set_dict[frozen_set] = [] + match_node_set_dict[frozen_set].append(match) + else: + match_node_set_dict[frozen_set].append(match) - if isinstance(instance, NetworkMapping): - print( - "Skipping Network Mapping: \n Input - {} \n Output - {}".format( - ",".join([n.ID for n in instance.input_nodes]), - ",".join([n.ID for n in instance.output_nodes]), - ) - ) - continue - else: - print("Applying Network Mapping: \n Nodes - {}".format(instance.node)) + # This is the explit match store that we keep track of explicitly defined mappings + explicit_matches: List[LibraryPrimitivesEntry] = [] - # Find the construction node assicated with the - # FIG node and then do the followinging: - # Step 1 - If the mappingtemplate has no technology string assiciated - # with the mapping, just apply the constraints to the associated mapping - # options + # This is the set of cover sets that are found and returned + explicit_cover_sets: List[Set[str]] = [] - # Step 2 - In there is a string assiciated with the mappingtemplate, we - # eliminate all mapping options that dont have a matching string / - # generate a mapping option with the corresponding + # Go through each of the explict matches, generate a subgraph and compare against + # all the matches + for explicit_mapping in explict_mappings: + # Only do the explicit mapping if the the mapping object has a technology + # associated with it else skip it + if explicit_mapping.technology_string is None: + continue - # In the case of an Fluid Value interaction put all valuenodes in the - # subgraph - node_ids.extend( - [ - fig.get_fignode(edge[0]).ID - for edge in fig.in_edges(instance.node.ID) - if isinstance(fig.get_fignode(edge[0]), ValueNode) - ] - ) - node_ids.append(instance.node.ID) - subgraph = fig.subgraph(node_ids) + # Generate a subgraph for each of the mapping instance fig + for instance in explicit_mapping.instances: + node_set = set() - # Get the Construction node that has the corresponding subgraph, - # and then replace the mapping option - cn = construction_graph.get_subgraph_cn(subgraph) - if primitive_to_use is not None: - mapping_option = MappingOption(primitive_to_use, subgraph) - cn.use_explicit_mapping(mapping_option) - cn_mapping_options.append(mapping_option) - else: - # Add the constraints to all the mapping options - # This is an example where since no explicit mapping - # was specified, we only add the performance/material - # constraints. This can be ulitized for whittling down - # options later if necessary. - cn_mapping_options.extend(cn.mapping_options) + # Check what kind of an instance this is + if isinstance(instance, NodeMappingInstance): + # This is a single node scenario + node_set.add(instance.node.ID) + elif isinstance(instance, FluidicOperatorMapping): + node_set.add(instance.node.ID) - # Now that we know what the mapping options are (either explicit - # loaded from the library, we can add the performance constraints) - for mapping_option in cn_mapping_options: - # Add all the constraints to the mapping_option - cn.constraints.extend(mapping.constraints) + elif isinstance(instance, StorageMapping): + node_set.add(instance.node.ID) + elif isinstance(instance, PumpMapping): + node_set.add(instance.node.ID) -def override_network_mappings( - mappings: List[NodeMappingTemplate], - mapping_library: MappingLibrary, - fig: FluidInteractionGraph, - construction_graph: ConstructionGraph, -) -> None: - # Go through the entire set of mappings in the FIG and generate / append the - # mapping options - # Step 1 - Loop through each of the mappingtemplates - # Step 2 - Loop through each of the instances in teh mappingtemplate - # Step 3 - Find the cn associated with each of the fig nodes and override the - # explicit mapping if mappingtemplate has an associated technology string - assign_node_index = 0 - for mapping in mappings: - for instance in mapping.instances: + elif isinstance(instance, NetworkMapping): + node_set = set() + node_set.union(set([node.ID for node in instance.input_nodes])) + node_set.union(set([node.ID for node in instance.output_nodes])) - primitive_to_use = None - if mapping.technology_string is not None: - # Create a mapping option from the library with the corresponding info - try: - primitive_to_use = mapping_library.get_primitive( - mapping.technology_string - ) - except Exception: - print( - "Could not find primitive with technology: {}".format( - mapping.technology_string - ) + if frozenset(node_set) in match_node_set_dict: + # This is an explicit match + # Remove the explicit match from the list of matches + print( + "Eliminating match: {}".format( + match_node_set_dict[frozenset(node_set)] ) - sys.exit(-100) - - node_ids = [] - cn = None # Get the right construction node for doing the stuff - cn_mapping_options = [] - - if isinstance(instance, NetworkMapping): + ) + match_node_set_dict[frozenset(node_set)].clear() + + # Now generate a match tuple for this instance + match_primitive_uid: Optional[str] = None + match_technology_string = explicit_mapping.technology_string + match_mapping: Dict[str, str] = {} + + # TODO - Retouch this part if we ever go into modifying how the matches are + # generated if we use the match string coordinates (use the match interface + # for this) (function - generate_single_match) + + # Check what kind of an instance this is + if isinstance(instance, NodeMappingInstance): + # This is a single node scenario + match_mapping[instance.node.ID] = "v1" + elif isinstance(instance, FluidicOperatorMapping): + match_mapping[instance.node.ID] = "v1" + + elif isinstance(instance, StorageMapping): + match_mapping[instance.node.ID] = "v1" + + elif isinstance(instance, PumpMapping): + match_mapping[instance.node.ID] = "v1" + + elif isinstance(instance, NetworkMapping): + for i in range(len(instance.input_nodes)): + node = instance.input_nodes[i] + match_mapping[node.ID] = f"vi{i}" + for i in range(len(instance.output_nodes)): + node = instance.output_nodes[i] + match_mapping[node.ID] = f"vo{i}" + + # Rewrite the matchid for the explicit matches + # based on the library entry + if frozenset(node_set) in match_node_set_dict: + # Find the primitive that matches the technology string + for primitive in match_node_set_dict[frozenset(node_set)]: + if primitive[1] == explicit_mapping.technology_string: + # This is the match we want to replace + # Replace the match id with the match tuple + match_primitive_uid = primitive[0] + # This is an explicit match + # Remove the explicit match from the list of matches print( - "Applying Network Mapping: \n Input - {} \n Output - {}".format( - ",".join([n.ID for n in instance.input_nodes]), - ",".join([n.ID for n in instance.output_nodes]), + "Eliminating match: {}".format( + match_node_set_dict[frozenset(node_set)] ) ) + match_node_set_dict[frozenset(node_set)].clear() - node_ids.extend(n.ID for n in instance.input_nodes) - node_ids.extend(n.ID for n in instance.output_nodes) - subgraph = fig.subgraph(node_ids) - # try: - # # TODO - Incase this is a flow-flow candidate, we need to get the - # # cn corresponding to this mapping. - # # TODO - do we need to have a new flow node constructed - - # cn = construction_graph.get_subgraph_cn(subgraph) - # except Exception as e: - # # Incase we cannot find a corresponding construction node, - # # we need to create a new construction node - # print(e) - # cn = ConstructionNode("assign_{}".format(assign_node_index)) - # # Increment the index of the assign construction node - # assign_node_index += 1 - - # # Find the cn's associated with the input nodes - # input_cns = [] - # output_cns = [] - # for fig_node in instance.input_nodes: - # cn_temp = construction_graph.get_fignode_cn(fig_node) - # if cn_temp not in input_cns: - # input_cns.append(cn_temp) - # for fig_node in instance.output_nodes: - # cn_temp = construction_graph.get_fignode_cn(fig_node) - # if cn_temp not in output_cns: - # output_cns.append(cn_temp) - - # # split_groups = [] - # # # TODO - If we need to split the we first are gonna make a copy - # # # of the subgraph and then delete any edges between the inputs - # # # and the outputs - # # subgraph_copy = deepcopy(subgraph) - # # # Delete any edges between inputs and outputs - # # for input_node in instance.input_nodes: - # # for output_node in instance.output_nodes: - # # if subgraph_copy.has_edge(input_node.id, output_node.id): - # # subgraph_copy.remove_edge(input_node.id, output_node.id) - - # # components = subgraph_copy.connected_components() - # # for component in components: - # # split_groups.append(list(component.nodes)) - - # # TODO - If inputcns and output cns are the same, we split them - # for input_cn in input_cns: - # if input_cn in output_cns: - # split_groups = generate_split_groups( - # input_cn.mapping_options[0].fig_subgraph, instance - # ) - # construction_graph.split_cn(input_cn, split_groups, fig) - # # Now insert the node - # construction_graph.insert_cn(cn, input_cns, output_cns) - - # # Check to see if this works or not - # cn = construction_graph.get_subgraph_cn(subgraph) - - # Find the cn's associated with the input nodes - input_cns = [] - output_cns = [] - for fig_node in instance.input_nodes: - cn_temp = construction_graph.get_fignode_cn(fig_node) - if cn_temp not in input_cns: - input_cns.append(cn_temp) - for fig_node in instance.output_nodes: - cn_temp = construction_graph.get_fignode_cn(fig_node) - if cn_temp not in output_cns: - output_cns.append(cn_temp) - - cn = ConstructionNode("assign_{}".format(assign_node_index)) - assign_node_index += 1 - if isinstance(primitive_to_use, NetworkPrimitive) is not True: - raise TypeError() - if primitive_to_use is None: - raise ValueError() - mapping_option = NetworkMappingOption( - network_primitive=primitive_to_use, - mapping_type=NetworkMappingOptionType.COMPONENT_REPLACEMENT, - subgraph_view=subgraph, + # If the match_primitive ID is None, we need to query a match from the + # library + if match_primitive_uid is None: + primitives_with_technology = library.get_primitives( + match_technology_string ) - cn.use_explicit_mapping(mapping_option) - construction_graph.insert_cn(cn, input_cns, output_cns, fig) - else: - continue - # Now that we know what the mapping options are (either explicit - # loaded from the library, we can add the performance constraints) - for mapping_option in cn_mapping_options: - # Add all the constraints to the mapping_option - cn.constraints.extend(mapping.constraints) - - -def eliminate_passthrough_nodes(construction_graph: ConstructionGraph): - for node_id in list(construction_graph.nodes): - cn = construction_graph.get_cn(node_id) - assert len(cn.mapping_options) == 1 - mapping_option = cn.mapping_options[0] - if isinstance(mapping_option, NetworkMappingOption): - if mapping_option.mapping_type is NetworkMappingOptionType.PASS_THROUGH: - - print("Eliminating PASS THROUGH construction node = {}".format(cn.ID)) - - # First get all the in and out edges - in_edges = list(construction_graph.in_edges(node_id)) - out_edges = list(construction_graph.out_edges(node_id)) - - # In Points - in_points = [in_edge[0] for in_edge in in_edges] - out_points = [out_edge[1] for out_edge in out_edges] - - # Create edges for the different cases - # Case 1 - 1->1 - if len(in_points) == 1 and len(out_points) == 1: - # Delete the node - construction_graph.delete_node(node_id) - construction_graph.add_edge(in_points[0], out_points[0]) - # Case 2 - n->1 - # Case 3 - 1->n - elif (len(in_points) > 1 and len(out_points) == 1) or ( - len(in_points) == 1 and len(out_points) > 1 - ): - # Delete the node - construction_graph.delete_node(node_id) - for in_point in in_points: - for out_point in out_points: - construction_graph.add_edge(in_point, out_point) - else: - raise Exception( - "Pass through network node elimination not implemented " - " when n->n edge creation is necessary" - ) + # TODO - We need to have a better way to pick between the primitives + # as a temprorary fix we just pick the first one + match_primitive_uid = primitives_with_technology[0].uid + # Add this match tuple to the list of matches + match_tuple: LibraryPrimitivesEntry = ( + match_primitive_uid, + match_technology_string, + match_mapping, + ) -def generate_split_groups(subgraph, instance) -> List[Set[str]]: - split_groups = [] - # TODO - If we need to split the we first are gonna make a copy - # of the subgraph and then delete any edges between the inputs - # and the outputs - subgraph_copy = deepcopy(subgraph) - # Delete delete all the input and output nodes here - for input_node in instance.input_nodes: - subgraph_copy.remove_node(input_node.id) + explicit_matches.append(match_tuple) + # This is something we need to return to the to the caller + explicit_cover_sets.append(node_set) - for output_node in instance.output_nodes: - subgraph_copy.remove_node(output_node.id) + # Modify the matches list + eliminated_matches = [] + for match_tuple_list in match_node_set_dict.values(): + for match_tuple in match_tuple_list: + eliminated_matches.append(match_tuple) - components = nx.connected_components(nx.to_undirected(subgraph_copy)) - for component in components: - split_groups.append(component) + # Add the explicit matches to the list of matches + eliminated_matches.extend(explicit_matches) - return split_groups + return (eliminated_matches, explicit_cover_sets) def connect_orphan_IO(): print("Implement the orphan io generation system") -def get_flow_flow_candidates( - module: Module, gen_strategy: GenStrategy -) -> List[ConstructionNode]: - """Get canddiates where it its a "flow" only sub graph - - Args: - module (Module): the module we want to check - gen_strategy (GenStrategy): the generation strategy we want to use - - Returns: - List[ConstructionNode]: List of all the construction nodes - """ - # TODO - go through all the edges and see which ones are between flow-flow graphs - # If these connectsions are between flow-flow nodes then we need to figure out - # which ones are part of the same network/connected graphs with only flow nodes - # The networks with only the flow nodes will need to be covered as a part of. - # these construction nodes. - - ret = [] - - # Step 1. Do a shallow copy of the graph - # Step 2. Remove all the fignodes that are not Flow - # Step 3. Now get the all the disconnected pieces of the graph - # Step 4. Create a Construction node for each of the disconnected pieces - # Return all the constructions nodes - - # Step 1. Do a shallow copy of the graph - fig_original = module.FIG - fig_copy = module.FIG.copy( - as_view=False - ) # Note this does not copy anything besides the nx.DiGraph at the moment - - # Step 2. Remove all the fignodes that are not Flow - remove_list = [] - - # Remove nodes from the explicit mapping construction nodes - for mapping in module.mappings: - for instance in mapping.instances: - if isinstance(instance, NetworkMapping): - remove_list.extend([n.ID for n in instance.input_nodes]) - remove_list.extend([n.ID for n in instance.output_nodes]) - else: - remove_list.append(instance.node.ID) - - for node_id in fig_copy.nodes: - node = fig_original.get_fignode(node_id) - if node.match_string != "FLOW": - remove_list.append(node_id) - - remove_list = list(set(remove_list)) - for node_id in remove_list: - fig_copy.remove_node(node_id) - - # Step 3. Now get the all the disconnected pieces of the graph - i = 0 - for component in nx.connected_components(fig_copy.to_undirected()): - print("Flow candidate") - print(component) - sub = fig_original.subgraph(component) - # TODO - Decide what the mapping type should be. for now assume that we just a - # single passthrough type scenario where we don't have to do much work - is_passthrough = __check_if_passthrough(sub) - if is_passthrough: - mapping_type = NetworkMappingOptionType.PASS_THROUGH - else: - mapping_type = NetworkMappingOptionType.CHANNEL_NETWORK - nprimitive = NetworkPrimitive(sub, gen_strategy) - nprimitive.generate_netlist() - mapping_option = NetworkMappingOption(nprimitive, mapping_type, sub) - # Step 4. Create a Construction node for each of the disconnected pieces - cn = ConstructionNode("flow_network_{}".format(i)) - cn.add_mapping_option(mapping_option) - - i += 1 - ret.append(cn) - - return ret - - def __check_if_passthrough(sub) -> bool: """Checks if its a passthrough chain diff --git a/lfr/netlistgenerator/mappinglibrary.py b/lfr/netlistgenerator/mappinglibrary.py index 2ab4021..4077c89 100644 --- a/lfr/netlistgenerator/mappinglibrary.py +++ b/lfr/netlistgenerator/mappinglibrary.py @@ -1,83 +1,149 @@ -from lfr.netlistgenerator.primitive import Primitive, ProceduralPrimitive from typing import Dict, List, Tuple + from lfr.fig.interaction import InteractionType +from lfr.netlistgenerator.connection_primitive import ConnectionPrimitive from lfr.netlistgenerator.primitive import Primitive, ProceduralPrimitive +MatchPatternEntry = Tuple[str, str, str] + class MappingLibrary: - def __init__(self, name) -> None: + """Mapping Lirbrary containing all the primitives we can match against""" + + def __init__(self, name: str) -> None: + """Initializes the mapping library. + + Args: + name (str): Name of the mapping library + """ self.__name = name - self.__mix_operators = [] - self.__meter_operators = [] - self.__seive_operators = [] - self.__dilute_operators = [] - self.__divide_operators = [] - self.__technology_process_operators = [] - self.__storage_primitives = [] - self.__pump_primitives = [] - self.__io_primitives = [] + self.__mix_operators: List[Primitive] = [] + self.__meter_operators: List[Primitive] = [] + self.__seive_operators: List[Primitive] = [] + self.__dilute_operators: List[Primitive] = [] + self.__divide_operators: List[Primitive] = [] + self.__technology_process_operators: List[Primitive] = [] + self.__storage_primitives: List[Primitive] = [] + self.__pump_primitives: List[Primitive] = [] + self.__io_primitives: List[Primitive] = [] self.__all_primitives: Dict[str, Primitive] = {} self.__procedural_primitves: List[ProceduralPrimitive] = [] + self.__connection_primitives: List[ConnectionPrimitive] = [] self._default_IO_primitive = None @property def name(self) -> str: + """Returns the name of the library. + + Returns: + str: Name of the library + """ return self.__name def add_io_entry(self, primitive: Primitive) -> None: + """Adds a primitive to the list of IO primitives. + + Args: + primitive (Primitive): Primitive to add to the list of IO primitives + """ self.__io_primitives.append(primitive) - self.__all_primitives[primitive.mint] = primitive + self.__all_primitives[primitive.uid] = primitive def add_operator_entry( self, primitive: Primitive, interaction_type: InteractionType ) -> None: + """Adds a primitive to the list of operators for the given interaction type. + + Args: + primitive (Primitive): Primitive to add to the list of operators + interaction_type (InteractionType): Type of interaction to add the primitive to + """ if interaction_type is InteractionType.MIX: self.__mix_operators.append(primitive) - self.__all_primitives[primitive.mint] = primitive + self.__all_primitives[primitive.uid] = primitive elif interaction_type is InteractionType.SIEVE: self.__seive_operators.append(primitive) - self.__all_primitives[primitive.mint] = primitive + self.__all_primitives[primitive.uid] = primitive elif interaction_type is InteractionType.DILUTE: self.__dilute_operators.append(primitive) - self.__all_primitives[primitive.mint] = primitive + self.__all_primitives[primitive.uid] = primitive elif interaction_type is InteractionType.METER: self.__meter_operators.append(primitive) - self.__all_primitives[primitive.mint] = primitive + self.__all_primitives[primitive.uid] = primitive elif interaction_type is InteractionType.DIVIDE: self.__divide_operators.append(primitive) - self.__all_primitives[primitive.mint] = primitive + self.__all_primitives[primitive.uid] = primitive else: self.__technology_process_operators.append(primitive) - self.__all_primitives[primitive.mint] = primitive + self.__all_primitives[primitive.uid] = primitive def add_entry(self, primitive: Primitive) -> None: - self.__all_primitives[primitive.mint] = primitive + """Adds a primitive to the library.""" + self.__all_primitives[primitive.uid] = primitive def add_procedural_entry(self, primitive: ProceduralPrimitive) -> None: + """Adds a procedural primitive to the library. + + Args: + primitive (ProceduralPrimitive): Primitive to add to the library + """ self.__procedural_primitves.append(primitive) - self.__all_primitives[primitive.mint] = primitive + self.__all_primitives[primitive.uid] = primitive def add_storage_entry(self, primitive: Primitive) -> None: + """Adds a primitive to the list of storage primitives. + + Args: + primitive (Primitive): Primitive to add to the list of storage primitives + """ self.__storage_primitives.append(primitive) - self.__all_primitives[primitive.mint] = primitive + self.__all_primitives[primitive.uid] = primitive def add_pump_entry(self, primitive: Primitive) -> None: + """Adds a primitive to the list of pump primitives. + + Args: + primitive (Primitive): Primitive to add to the list of pump primitives + """ self.__pump_primitives.append(primitive) - self.__all_primitives[primitive.mint] = primitive + self.__all_primitives[primitive.uid] = primitive def get_storage_entries(self) -> List[Primitive]: + """Returns the list of storage primitives. + + Returns: + List[Primitive]: List of storage primitives + """ return self.__storage_primitives def get_pump_entries(self) -> List[Primitive]: + """Returns the list of pump primitives. + + Returns: + List[Primitive]: List of pump primitives + """ return self.__pump_primitives def get_default_IO(self) -> Primitive: + """Returns the default IO primitive. + + Returns: + Primitive: Default IO primitive + """ if self._default_IO_primitive is None: return self.__io_primitives[0] else: return self._default_IO_primitive def get_operators(self, interaction_type: InteractionType) -> List[Primitive]: + """Gets the primitives for the given interaction type. + + Args: + interaction_type (InteractionType): Type of interaction to get primitives + + Returns: + List[Primitive]: List of primitives for the given interaction type + """ if interaction_type is InteractionType.MIX: return self.__mix_operators elif interaction_type is InteractionType.SIEVE: @@ -91,15 +157,86 @@ def get_operators(self, interaction_type: InteractionType) -> List[Primitive]: else: return self.__technology_process_operators - def get_primitive(self, technology_string: str) -> Primitive: - return self.__all_primitives[technology_string] + def get_primitive(self, uid: str) -> Primitive: + """Returns the primitive with the given uid. + + Args: + uid (str): UID of the primitive to return + + Raises: + KeyError: If the primitive is not found + + Returns: + Primitive: The primitive with the given uid + """ + if uid in self.__all_primitives: + return self.__all_primitives[uid] + + raise KeyError("No primitive with uid: " + uid) + + def get_primitives(self, technology_string: str) -> List[Primitive]: + """Get the primitive with the given technology string. + + + Args: + technology_string (str): MINT String around which the + primitive is defined + + Returns: + List[Primitive]: List of primitives with the given technology string. + Returns an empty list if no primitives are found. + """ + ret = [] + for primitive in self.__all_primitives.values(): + if primitive.mint == technology_string: + ret.append(primitive) + return ret def has_primitive(self, technology_string: str) -> bool: - return technology_string in self.__all_primitives.keys() + """Checks whether the library contains a primitive with the given technology string. + + + Args: + technology_string (str): MINT String of the technology - def get_match_patterns(self) -> List[Tuple[str, str]]: + Returns: + bool: whether it exists or not + """ + # Go through each of the entries in the all_primitives dictionary and see if the + # technology string is in there. + ret = False + for primitive in self.__all_primitives.values(): + if primitive.mint == technology_string: + ret = True + break + + return ret + + def get_match_patterns(self) -> List[MatchPatternEntry]: + """Returns the match patterns for the library. + + Returns: + List[MatchPattern]: Match patterns for the library + """ ret = [] for primitive in self.__all_primitives.values(): - ret.append((primitive.mint, primitive.match_string)) + ret.append((primitive.uid, primitive.mint, primitive.match_string)) return ret + + def add_connection_entry(self, primitive: ConnectionPrimitive) -> None: + """Adds a primitive to the list of connection primitives. + + Args: + primitive (Primitive): Primitive to add to the list of connection primitives + """ + self.__connection_primitives.append(primitive) + self.__all_primitives[primitive.uid] = primitive + + def get_default_connection_entry(self) -> ConnectionPrimitive: + """Returns the default connection primitive. + + Returns: + ConnectionPrimitive: Default connection primitive + """ + return self.__connection_primitives[0] diff --git a/lfr/netlistgenerator/mappinglibrary_generator.py b/lfr/netlistgenerator/mappinglibrary_generator.py new file mode 100644 index 0000000..bfa8cba --- /dev/null +++ b/lfr/netlistgenerator/mappinglibrary_generator.py @@ -0,0 +1,936 @@ +from typing import Dict, List + +from lfr.fig.interaction import InteractionType +from lfr.netlistgenerator.connectingoption import ConnectingOption +from lfr.netlistgenerator.connection_primitive import ConnectionPrimitive +from lfr.netlistgenerator.mappinglibrary import MappingLibrary +from lfr.netlistgenerator.primitive import Primitive, PrimitiveType +from lfr.netlistgenerator.procedural_component_algorithms.ytree import YTREE + + +def generate_mlsi_library() -> MappingLibrary: + library = MappingLibrary("mlsi") + # PORT + port_inputs: List[ConnectingOption] = [] + port_inputs.append(ConnectingOption(None, [None])) + port_inputs.append(ConnectingOption(None, [None])) + port_inputs.append(ConnectingOption(None, [None])) + port_inputs.append(ConnectingOption(None, [None])) + port_inputs.append(ConnectingOption(None, [None])) + port_inputs.append(ConnectingOption(None, [None])) + port_inputs.append(ConnectingOption(None, [None])) + port_inputs.append(ConnectingOption(None, [None])) + port_inputs.append(ConnectingOption(None, [None])) + port_inputs.append(ConnectingOption(None, [None])) + port_inputs.append(ConnectingOption(None, [None])) + port_inputs.append(ConnectingOption(None, [None])) + port_inputs.append(ConnectingOption(None, [None])) + port_inputs.append(ConnectingOption(None, [None])) + port_inputs.append(ConnectingOption(None, [None])) + port_inputs.append(ConnectingOption(None, [None])) + port_inputs.append(ConnectingOption(None, [None])) + port_inputs.append(ConnectingOption(None, [None])) + port_inputs.append(ConnectingOption(None, [None])) + + port_outputs: List[ConnectingOption] = [] + port_outputs.append(ConnectingOption(None, [])) + port_outputs.append(ConnectingOption(None, [])) + port_outputs.append(ConnectingOption(None, [])) + port_outputs.append(ConnectingOption(None, [])) + port_outputs.append(ConnectingOption(None, [])) + port_outputs.append(ConnectingOption(None, [])) + port_outputs.append(ConnectingOption(None, [])) + port_outputs.append(ConnectingOption(None, [])) + port_outputs.append(ConnectingOption(None, [])) + port_outputs.append(ConnectingOption(None, [])) + port_outputs.append(ConnectingOption(None, [])) + port_outputs.append(ConnectingOption(None, [])) + port_outputs.append(ConnectingOption(None, [])) + port_outputs.append(ConnectingOption(None, [])) + port_outputs.append(ConnectingOption(None, [])) + port_outputs.append(ConnectingOption(None, [])) + port_outputs.append(ConnectingOption(None, [])) + port_outputs.append(ConnectingOption(None, [])) + port_outputs.append(ConnectingOption(None, [])) + + port = Primitive( + "PORT", + PrimitiveType.COMPONENT, + r"""{ + v1:IO + }""", + False, + False, + port_inputs, + port_outputs, + None, + None, + None, + ) + + library.add_io_entry(port) + + # MIXER - CONTINOUS FLOW ONE + + cf_mixer_inputs: List[ConnectingOption] = [] + + cf_mixer_inputs.append(ConnectingOption(None, ["1"])) + cf_mixer_inputs.append(ConnectingOption(None, ["1"])) + cf_mixer_inputs.append(ConnectingOption(None, ["1"])) + cf_mixer_inputs.append(ConnectingOption(None, ["1"])) + cf_mixer_inputs.append(ConnectingOption(None, ["1"])) + cf_mixer_inputs.append(ConnectingOption(None, ["1"])) + cf_mixer_inputs.append(ConnectingOption(None, ["1"])) + cf_mixer_inputs.append(ConnectingOption(None, ["1"])) + cf_mixer_inputs.append(ConnectingOption(None, ["1"])) + cf_mixer_inputs.append(ConnectingOption(None, ["1"])) + + cf_mixer_outputs: List[ConnectingOption] = [] + + cf_mixer_outputs.append(ConnectingOption(None, ["2"])) + + cf_mixer_loadings: List[ConnectingOption] = [] + cf_mixer_carriers: List[ConnectingOption] = [] + + cf_mixer = Primitive( + "MIXER", + PrimitiveType.COMPONENT, + r"""{ + v1:MIX + }""", + False, + False, + cf_mixer_inputs, + cf_mixer_outputs, + cf_mixer_loadings, + cf_mixer_carriers, + None, + ) + + library.add_operator_entry(cf_mixer, InteractionType.MIX) + + # MUX2 + + mux2_inputs: List[ConnectingOption] = [] + mux2_inputs.append(ConnectingOption(None, ["1"])) + + mux2_outputs: List[ConnectingOption] = [] + mux2_outputs.append(ConnectingOption(None, ["2"])) + mux2_outputs.append(ConnectingOption(None, ["3"])) + + mux2 = Primitive( + "MUX", + PrimitiveType.COMPONENT, + r"""{ + v1 { "DISTRIBUTE_OR", "or_1" }, + v1 -> vo1 { "DISTRIBUTE_OR", "or_1" }, + v1 -> vo2 { "DISTRIBUTE_OR", "or_1" } + } + """, + False, + False, + mux2_inputs, + mux2_outputs, + None, + None, + None, + ) + + library.add_entry(mux2) + + return library + + +def generate_mars_library() -> MappingLibrary: + library = MappingLibrary("mars") + + # PORT + port_inputs: List[ConnectingOption] = [] + port_inputs.append(ConnectingOption(None, [None])) + port_inputs.append(ConnectingOption(None, [None])) + port_inputs.append(ConnectingOption(None, [None])) + port_inputs.append(ConnectingOption(None, [None])) + port_inputs.append(ConnectingOption(None, [None])) + port_inputs.append(ConnectingOption(None, [None])) + port_inputs.append(ConnectingOption(None, [None])) + port_inputs.append(ConnectingOption(None, [None])) + port_inputs.append(ConnectingOption(None, [None])) + port_inputs.append(ConnectingOption(None, [None])) + port_inputs.append(ConnectingOption(None, [None])) + port_inputs.append(ConnectingOption(None, [None])) + port_inputs.append(ConnectingOption(None, [None])) + port_inputs.append(ConnectingOption(None, [None])) + port_inputs.append(ConnectingOption(None, [None])) + port_inputs.append(ConnectingOption(None, [None])) + port_inputs.append(ConnectingOption(None, [None])) + port_inputs.append(ConnectingOption(None, [None])) + port_inputs.append(ConnectingOption(None, [None])) + + port_outputs: List[ConnectingOption] = [] + port_outputs.append(ConnectingOption(None, [])) + port_outputs.append(ConnectingOption(None, [])) + port_outputs.append(ConnectingOption(None, [])) + port_outputs.append(ConnectingOption(None, [])) + port_outputs.append(ConnectingOption(None, [])) + port_outputs.append(ConnectingOption(None, [])) + port_outputs.append(ConnectingOption(None, [])) + port_outputs.append(ConnectingOption(None, [])) + port_outputs.append(ConnectingOption(None, [])) + port_outputs.append(ConnectingOption(None, [])) + port_outputs.append(ConnectingOption(None, [])) + port_outputs.append(ConnectingOption(None, [])) + port_outputs.append(ConnectingOption(None, [])) + port_outputs.append(ConnectingOption(None, [])) + port_outputs.append(ConnectingOption(None, [])) + port_outputs.append(ConnectingOption(None, [])) + port_outputs.append(ConnectingOption(None, [])) + port_outputs.append(ConnectingOption(None, [])) + port_outputs.append(ConnectingOption(None, [])) + + port = Primitive( + "PORT", + PrimitiveType.COMPONENT, + "IO", + False, + False, + port_inputs, + port_outputs, + None, + None, + None, + ) + + library.add_io_entry(port) + + # NORMAL MIXER + + mixer_inputs: List[ConnectingOption] = [] + + mixer_inputs.append(ConnectingOption(None, ["1"])) + mixer_inputs.append(ConnectingOption(None, ["1"])) + mixer_inputs.append(ConnectingOption(None, ["1"])) + mixer_inputs.append(ConnectingOption(None, ["1"])) + mixer_inputs.append(ConnectingOption(None, ["1"])) + mixer_inputs.append(ConnectingOption(None, ["1"])) + mixer_inputs.append(ConnectingOption(None, ["1"])) + mixer_inputs.append(ConnectingOption(None, ["1"])) + mixer_inputs.append(ConnectingOption(None, ["1"])) + mixer_inputs.append(ConnectingOption(None, ["1"])) + + mixer_outputs: List[ConnectingOption] = [] + + mixer_outputs.append(ConnectingOption(None, ["2"])) + mixer_outputs.append(ConnectingOption(None, ["2"])) + mixer_outputs.append(ConnectingOption(None, ["2"])) + mixer_outputs.append(ConnectingOption(None, ["2"])) + mixer_outputs.append(ConnectingOption(None, ["2"])) + mixer_outputs.append(ConnectingOption(None, ["2"])) + mixer_outputs.append(ConnectingOption(None, ["2"])) + mixer_outputs.append(ConnectingOption(None, ["2"])) + mixer_outputs.append(ConnectingOption(None, ["2"])) + mixer_outputs.append(ConnectingOption(None, ["2"])) + + mixer_loadings: List[ConnectingOption] = [] + mixer_carriers: List[ConnectingOption] = [] + + mixer = Primitive( + "MIXER", + PrimitiveType.COMPONENT, + "MIX", + False, + False, + mixer_inputs, + mixer_outputs, + mixer_loadings, + mixer_carriers, + None, + ) + + library.add_operator_entry(mixer, InteractionType.MIX) + + # DIAMOND REACTION CHAMBER + + diamond_chamber_inputs: List[ConnectingOption] = [] + + diamond_chamber_inputs.append(ConnectingOption("default_component", ["1"])) + + diamond_chamber_outputs: List[ConnectingOption] = [] + + diamond_chamber_outputs.append(ConnectingOption("default_component", ["2"])) + + diamond_chamber_loadings: List[ConnectingOption] = [] + diamond_chamber_carriers: List[ConnectingOption] = [] + + diamond_chamber = Primitive( + "DIAMOND REACTION CHAMBER", + PrimitiveType.COMPONENT, + "PROCESS", + False, + False, + diamond_chamber_inputs, + diamond_chamber_outputs, + diamond_chamber_loadings, + diamond_chamber_carriers, + None, + ) + + library.add_operator_entry(diamond_chamber, InteractionType.TECHNOLOGY_PROCESS) + + # METER + + meter_inputs: List[ConnectingOption] = [] + + meter_outputs: List[ConnectingOption] = [] + + meter_outputs.append(ConnectingOption("default_component", ["1"])) + + meter_loadings: List[ConnectingOption] = [] + + meter_loadings.append(ConnectingOption("default_component", ["2"])) + + meter_carriers: List[ConnectingOption] = [] + + meter_carriers.append(ConnectingOption("default_component", ["3"])) + + meter = Primitive( + "METER", + PrimitiveType.NETLIST, + "METER", + False, + False, + meter_inputs, + meter_outputs, + meter_loadings, + meter_carriers, + "default-netlists/dropletgenerator.mint", + ["droplet_size", "generation_rate"], + [ + "orifice_size", + "aspect_ratio", + "capillary_number", + "expansion_ratio", + "flow_rate_ratio", + "normalized_oil_inlet", + "normalized_orifice_length", + "normalized_water_inlet", + ], + ) + + library.add_operator_entry(meter, InteractionType.METER) + + # Incubator + + incubator_inputs: List[ConnectingOption] = [] + + incubator_inputs.append(ConnectingOption("default_component", ["1"])) + + incubator_outputs: List[ConnectingOption] = [] + + incubator_outputs.append(ConnectingOption("default_component", ["1"])) + + incubator_loadings: List[ConnectingOption] = [] + incubator_carriers: List[ConnectingOption] = [] + + incubator = Primitive( + "INCUBATOR", + PrimitiveType.COMPONENT, + "PROCESS", + False, + False, + incubator_inputs, + incubator_outputs, + incubator_loadings, + incubator_carriers, + ) + + library.add_operator_entry(incubator, InteractionType.TECHNOLOGY_PROCESS) + + # SORTER + + sorter_inputs: List[ConnectingOption] = [] + + sorter_inputs.append(ConnectingOption(None, ["1"])) + + sorter_outputs: List[ConnectingOption] = [] + + sorter_outputs.append(ConnectingOption(None, ["2"])) + sorter_outputs.append(ConnectingOption(None, ["3"])) + + sorter_loadings: List[ConnectingOption] = [] + sorter_carriers: List[ConnectingOption] = [] + + # TODO - Modify this later on + sorter = Primitive( + "DROPLET SORTER", + PrimitiveType.COMPONENT, + "SIEVE", + False, + False, + sorter_inputs, + sorter_outputs, + sorter_loadings, + sorter_carriers, + None, + ) + + library.add_operator_entry(sorter, InteractionType.SIEVE) + + return library + + +def generate_dropx_library() -> MappingLibrary: + library = MappingLibrary("dropx") + + # PORT + port_inputs: List[ConnectingOption] = [] + port_inputs.append(ConnectingOption(None, [None])) + port_inputs.append(ConnectingOption(None, [None])) + port_inputs.append(ConnectingOption(None, [None])) + port_inputs.append(ConnectingOption(None, [None])) + port_inputs.append(ConnectingOption(None, [None])) + port_inputs.append(ConnectingOption(None, [None])) + port_inputs.append(ConnectingOption(None, [None])) + port_inputs.append(ConnectingOption(None, [None])) + port_inputs.append(ConnectingOption(None, [None])) + port_inputs.append(ConnectingOption(None, [None])) + port_inputs.append(ConnectingOption(None, [None])) + port_inputs.append(ConnectingOption(None, [None])) + port_inputs.append(ConnectingOption(None, [None])) + port_inputs.append(ConnectingOption(None, [None])) + port_inputs.append(ConnectingOption(None, [None])) + port_inputs.append(ConnectingOption(None, [None])) + port_inputs.append(ConnectingOption(None, [None])) + port_inputs.append(ConnectingOption(None, [None])) + port_inputs.append(ConnectingOption(None, [None])) + + port_outputs: List[ConnectingOption] = [] + port_outputs.append(ConnectingOption(None, [])) + port_outputs.append(ConnectingOption(None, [])) + port_outputs.append(ConnectingOption(None, [])) + port_outputs.append(ConnectingOption(None, [])) + port_outputs.append(ConnectingOption(None, [])) + port_outputs.append(ConnectingOption(None, [])) + port_outputs.append(ConnectingOption(None, [])) + port_outputs.append(ConnectingOption(None, [])) + port_outputs.append(ConnectingOption(None, [])) + port_outputs.append(ConnectingOption(None, [])) + port_outputs.append(ConnectingOption(None, [])) + port_outputs.append(ConnectingOption(None, [])) + port_outputs.append(ConnectingOption(None, [])) + port_outputs.append(ConnectingOption(None, [])) + port_outputs.append(ConnectingOption(None, [])) + port_outputs.append(ConnectingOption(None, [])) + port_outputs.append(ConnectingOption(None, [])) + port_outputs.append(ConnectingOption(None, [])) + port_outputs.append(ConnectingOption(None, [])) + + port = Primitive( + "PORT", + PrimitiveType.COMPONENT, + r"""{ + v1:IO + }""", + False, + False, + port_inputs, + port_outputs, + None, + None, + None, + ) + + library.add_io_entry(port) + + # PICO INJECTOR + + pico_injector_inputs: List[ConnectingOption] = [] + + pico_injector_inputs.append(ConnectingOption(None, ["1"])) + pico_injector_inputs.append(ConnectingOption(None, ["2"])) + + pico_injector_outputs: List[ConnectingOption] = [] + + pico_injector_outputs.append(ConnectingOption(None, ["3"])) + + pico_injector_loadings: List[ConnectingOption] = [] + pico_injector_carriers: List[ConnectingOption] = [] + + pico_injector = Primitive( + "PICOINJECTOR", + PrimitiveType.COMPONENT, + r"""{ + v1:MIX + }""", + False, + False, + pico_injector_inputs, + pico_injector_outputs, + pico_injector_loadings, + pico_injector_carriers, + None, + ) + + library.add_operator_entry(pico_injector, InteractionType.MIX) + + # DROPLET ELECTROPHORESIS MERGER + + electrophoresis_merger_inputs: List[ConnectingOption] = [] + + electrophoresis_merger_inputs.append(ConnectingOption(None, ["1"])) + electrophoresis_merger_inputs.append(ConnectingOption(None, ["2"])) + + electrophoresis_merger_outputs: List[ConnectingOption] = [] + + electrophoresis_merger_outputs.append(ConnectingOption(None, ["3"])) + + electrophoresis_merger_loadings: List[ConnectingOption] = [] + electrophoresis_merger_carriers: List[ConnectingOption] = [] + + # TODO - Modify this later on + electrophoresis_merger = Primitive( + "DROPLET MERGER", + PrimitiveType.COMPONENT, + r"""{ + v1:MIX + }""", + False, + False, + electrophoresis_merger_inputs, + electrophoresis_merger_outputs, + electrophoresis_merger_loadings, + electrophoresis_merger_carriers, + None, + ) + + library.add_operator_entry(electrophoresis_merger, InteractionType.MIX) + + # DROPLET SORTER + + droplet_sorter_inputs: List[ConnectingOption] = [] + + droplet_sorter_inputs.append(ConnectingOption(None, ["1"])) + + droplet_sorter_outputs: List[ConnectingOption] = [] + + droplet_sorter_outputs.append(ConnectingOption(None, ["2"])) + droplet_sorter_outputs.append(ConnectingOption(None, ["3"])) + + droplet_sorter_loadings: List[ConnectingOption] = [] + droplet_sorter_carriers: List[ConnectingOption] = [] + + # TODO - Modify this later on + droplet_sorter = Primitive( + "DROPLET SORTER", + PrimitiveType.COMPONENT, + r"""{ + v1:SIEVE + }""", + False, + False, + droplet_sorter_inputs, + droplet_sorter_outputs, + droplet_sorter_loadings, + droplet_sorter_carriers, + None, + ) + + library.add_operator_entry(droplet_sorter, InteractionType.SIEVE) + + # DROPLET GENERATOR + + droplet_generator_inputs: List[ConnectingOption] = [] + + droplet_generator_inputs.append(ConnectingOption("default_component", ["1"])) + + droplet_generator_outputs: List[ConnectingOption] = [] + + droplet_generator_outputs.append(ConnectingOption("default_component", ["3"])) + + droplet_generator_loadings: List[ConnectingOption] = [] + droplet_generator_carriers: List[ConnectingOption] = [] + + droplet_generator = Primitive( + "NOZZLE DROPLET GENERATOR", + PrimitiveType.NETLIST, + r"""{ + v1:METER + }""", + False, + False, + droplet_generator_inputs, + droplet_generator_outputs, + droplet_generator_loadings, + droplet_generator_carriers, + "default-netlists/dropletgenerator.mint", + ["droplet_size", "generation_rate"], + [ + "orifice_size", + "aspect_ratio", + "capillary_number", + "expansion_ratio", + "flow_rate_ratio", + "normalized_oil_inlet", + "normalized_orifice_length", + "normalized_water_inlet", + ], + ) + + library.add_operator_entry(droplet_generator, InteractionType.METER) + + droplet_merger_junction_inputs: List[ConnectingOption] = [] + + droplet_merger_junction_inputs.append(ConnectingOption(None, ["1"])) + droplet_merger_junction_inputs.append(ConnectingOption(None, ["2"])) + + droplet_merger_junction_outputs: List[ConnectingOption] = [] + + droplet_merger_junction_outputs.append(ConnectingOption(None, ["3"])) + + droplet_merger_junction_loadings: List[ConnectingOption] = [] + droplet_merger_junction_carriers: List[ConnectingOption] = [] + + droplet_merger_junction = Primitive( + "DROPLET MERGER JUNCTION", + PrimitiveType.COMPONENT, + r"""{ + v1:MIX + }""", + False, + False, + droplet_merger_junction_inputs, + droplet_merger_junction_outputs, + droplet_merger_junction_loadings, + droplet_merger_junction_carriers, + None, + ) + + library.add_operator_entry(droplet_merger_junction, InteractionType.MIX) + + # DROPLET MERGER CHANNEL + + droplet_merger_channel_inputs: List[ConnectingOption] = [] + + droplet_merger_channel_inputs.append(ConnectingOption(None, ["1"])) + + droplet_merger_channel_outputs: List[ConnectingOption] = [] + + droplet_merger_channel_outputs.append(ConnectingOption(None, ["2"])) + + droplet_merger_channel_loadings: List[ConnectingOption] = [] + droplet_merger_channel_carriers: List[ConnectingOption] = [] + + droplet_merger_channel = Primitive( + "DROPLET MERGER CHANNEL", + PrimitiveType.COMPONENT, + r"""{ + v1:MIX + }""", + False, + False, + droplet_merger_channel_inputs, + droplet_merger_channel_outputs, + droplet_merger_channel_loadings, + droplet_merger_channel_carriers, + None, + ) + + library.add_operator_entry(droplet_merger_channel, InteractionType.MIX) + + # MIXER - CONTINOUS FLOW ONE + + cf_mixer_inputs: List[ConnectingOption] = [] + + cf_mixer_inputs.append(ConnectingOption(None, ["1"])) + cf_mixer_inputs.append(ConnectingOption(None, ["1"])) + cf_mixer_inputs.append(ConnectingOption(None, ["1"])) + cf_mixer_inputs.append(ConnectingOption(None, ["1"])) + cf_mixer_inputs.append(ConnectingOption(None, ["1"])) + cf_mixer_inputs.append(ConnectingOption(None, ["1"])) + cf_mixer_inputs.append(ConnectingOption(None, ["1"])) + cf_mixer_inputs.append(ConnectingOption(None, ["1"])) + cf_mixer_inputs.append(ConnectingOption(None, ["1"])) + cf_mixer_inputs.append(ConnectingOption(None, ["1"])) + + cf_mixer_outputs: List[ConnectingOption] = [] + + cf_mixer_outputs.append(ConnectingOption(None, ["2"])) + + cf_mixer_loadings: List[ConnectingOption] = [] + cf_mixer_carriers: List[ConnectingOption] = [] + + cf_mixer = Primitive( + "MIXER", + PrimitiveType.COMPONENT, + r"""{ + v1:MIX + }""", + False, + False, + cf_mixer_inputs, + cf_mixer_outputs, + cf_mixer_loadings, + cf_mixer_carriers, + None, + ) + + library.add_operator_entry(cf_mixer, InteractionType.MIX) + + # DROPLET SPLITTER + + droplet_splitter_inputs: List[ConnectingOption] = [] + + droplet_splitter_inputs.append(ConnectingOption(None, ["1"])) + + droplet_splitter_outputs: List[ConnectingOption] = [] + + droplet_splitter_outputs.append(ConnectingOption(None, ["2"])) + droplet_splitter_outputs.append(ConnectingOption(None, ["3"])) + + droplet_splitter_loadings: List[ConnectingOption] = [] + droplet_splitter_carriers: List[ConnectingOption] = [] + + droplet_splitter = Primitive( + "DROPLET SPLITTER", + PrimitiveType.COMPONENT, + r"""{ + v1:DIVIDE + }""", + False, + False, + droplet_splitter_inputs, + droplet_splitter_outputs, + droplet_splitter_loadings, + droplet_splitter_carriers, + None, + ) + + library.add_operator_entry(droplet_splitter, InteractionType.DIVIDE) + + # NORMAL MIXER + + mixer_inputs: List[ConnectingOption] = [] + + mixer_inputs.append(ConnectingOption(None, ["1"])) + mixer_inputs.append(ConnectingOption(None, ["1"])) + mixer_inputs.append(ConnectingOption(None, ["1"])) + mixer_inputs.append(ConnectingOption(None, ["1"])) + mixer_inputs.append(ConnectingOption(None, ["1"])) + mixer_inputs.append(ConnectingOption(None, ["1"])) + mixer_inputs.append(ConnectingOption(None, ["1"])) + mixer_inputs.append(ConnectingOption(None, ["1"])) + mixer_inputs.append(ConnectingOption(None, ["1"])) + mixer_inputs.append(ConnectingOption(None, ["1"])) + + mixer_outputs: List[ConnectingOption] = [] + + mixer_outputs.append(ConnectingOption(None, ["2"])) + mixer_outputs.append(ConnectingOption(None, ["2"])) + mixer_outputs.append(ConnectingOption(None, ["2"])) + mixer_outputs.append(ConnectingOption(None, ["2"])) + mixer_outputs.append(ConnectingOption(None, ["2"])) + mixer_outputs.append(ConnectingOption(None, ["2"])) + mixer_outputs.append(ConnectingOption(None, ["2"])) + mixer_outputs.append(ConnectingOption(None, ["2"])) + mixer_outputs.append(ConnectingOption(None, ["2"])) + mixer_outputs.append(ConnectingOption(None, ["2"])) + + mixer_loadings: List[ConnectingOption] = [] + mixer_carriers: List[ConnectingOption] = [] + + mixer = Primitive( + "MIXER", + PrimitiveType.COMPONENT, + r"""{ + v1:MIX + }""", + False, + False, + mixer_inputs, + mixer_outputs, + mixer_loadings, + mixer_carriers, + None, + ) + + library.add_operator_entry(mixer, InteractionType.MIX) + + # DROPLET CAPACITANCE SENSOR + + droplet_capacitance_sensor_inputs: List[ConnectingOption] = [] + + droplet_capacitance_sensor_inputs.append(ConnectingOption(None, ["1"])) + + droplet_capacitance_sensor_outputs: List[ConnectingOption] = [] + + droplet_capacitance_sensor_outputs.append(ConnectingOption(None, ["2"])) + + droplet_capacitance_sensor_loadings: List[ConnectingOption] = [] + droplet_capacitance_sensor_carriers: List[ConnectingOption] = [] + + droplet_capacitance_sensor = Primitive( + "DROPLET CAPACITANCE SENSOR", + PrimitiveType.COMPONENT, + r"""{ + v1:PROCESS + }""", + False, + False, + droplet_capacitance_sensor_inputs, + droplet_capacitance_sensor_outputs, + droplet_capacitance_sensor_loadings, + droplet_capacitance_sensor_carriers, + None, + ) + + library.add_operator_entry( + droplet_capacitance_sensor, InteractionType.TECHNOLOGY_PROCESS + ) + + # FILTER + + filter_inputs: List[ConnectingOption] = [] + + filter_inputs.append(ConnectingOption(None, ["1"])) + + filter_outputs: List[ConnectingOption] = [] + + filter_outputs.append(ConnectingOption(None, ["2"])) + + filter_loadings: List[ConnectingOption] = [] + filter_carriers: List[ConnectingOption] = [] + + filter = Primitive( + "FILTER", + PrimitiveType.COMPONENT, + r"""{ + v1:PROCESS + }""", + False, + False, + filter_inputs, + filter_outputs, + filter_loadings, + filter_carriers, + None, + ) + + library.add_operator_entry(filter, InteractionType.TECHNOLOGY_PROCESS) + + # DROPLET FLUORESCENCE SENSOR + + droplet_fluorescence_sensor_inputs: List[ConnectingOption] = [] + + droplet_fluorescence_sensor_inputs.append(ConnectingOption(None, ["1"])) + + droplet_fluorescence_sensor_outputs: List[ConnectingOption] = [] + + droplet_fluorescence_sensor_outputs.append(ConnectingOption(None, ["2"])) + + droplet_fluorescence_sensor_loadings: List[ConnectingOption] = [] + droplet_fluorescence_sensor_carriers: List[ConnectingOption] = [] + + droplet_fluorescence_sensor = Primitive( + "DROPLET FLUORESCENCE SENSOR", + PrimitiveType.COMPONENT, + r"""{ + v1:PROCESS + }""", + False, + False, + droplet_fluorescence_sensor_inputs, + droplet_fluorescence_sensor_outputs, + droplet_fluorescence_sensor_loadings, + droplet_fluorescence_sensor_carriers, + None, + ) + + library.add_operator_entry( + droplet_fluorescence_sensor, InteractionType.TECHNOLOGY_PROCESS + ) + + # DROPLET LUMINESCENCE SENSOR + droplet_luminescence_sensor_inputs: List[ConnectingOption] = [] + + droplet_luminescence_sensor_inputs.append(ConnectingOption(None, ["1"])) + + droplet_luminescence_sensor_outputs: List[ConnectingOption] = [] + + droplet_luminescence_sensor_outputs.append(ConnectingOption(None, ["2"])) + + droplet_luminescence_sensor_loadings: List[ConnectingOption] = [] + droplet_luminescence_sensor_carriers: List[ConnectingOption] = [] + + droplet_luminescence_sensor = Primitive( + "DROPLET LUMINESCENCE SENSOR", + PrimitiveType.COMPONENT, + r"""{ + v1:PROCESS + }""", + False, + False, + droplet_luminescence_sensor_inputs, + droplet_luminescence_sensor_outputs, + droplet_luminescence_sensor_loadings, + droplet_luminescence_sensor_carriers, + None, + ) + + library.add_operator_entry( + droplet_luminescence_sensor, InteractionType.TECHNOLOGY_PROCESS + ) + + # DROPLET SPACER + + droplet_spacer_inputs: List[ConnectingOption] = [] + + droplet_spacer_inputs.append(ConnectingOption("default_component", ["1"])) + + droplet_spacer_outputs: List[ConnectingOption] = [] + + droplet_spacer_outputs.append(ConnectingOption("default_component", ["2"])) + + droplet_spacer_loadings: List[ConnectingOption] = [] + droplet_spacer_carriers: List[ConnectingOption] = [] + + droplet_spacer = Primitive( + "DROPLET SPACER", + PrimitiveType.NETLIST, + r"""{ + v1:PROCESS + }""", + False, + False, + droplet_spacer_inputs, + droplet_spacer_outputs, + droplet_spacer_loadings, + droplet_spacer_carriers, + "default-netlists/dropletspacer.mint", + ) + + library.add_operator_entry(droplet_spacer, InteractionType.TECHNOLOGY_PROCESS) + + # YTREE - This is a procedural primitives + + ytree = YTREE() + + library.add_procedural_entry(ytree) + + # Connections / Channels + connection_primitive = ConnectionPrimitive("CHANNEL") + library.add_connection_entry(connection_primitive) + + return library + + +# def generate_MARS_library() -> MappingLibrary: +# # TODO - Programatically create each of the items necessary for the MARS +# primitive library, +# # we shall serialize them after experimentation + +# # mix_primitive = MappingOption() + + +# # mix_primitive.init_single_component(mint_component) + +# # mix_primitive.add +# pass diff --git a/lfr/netlistgenerator/mappingoption.py b/lfr/netlistgenerator/mappingoption.py index 44dffc6..d6ebd0b 100644 --- a/lfr/netlistgenerator/mappingoption.py +++ b/lfr/netlistgenerator/mappingoption.py @@ -1,17 +1,18 @@ from typing import Optional -from networkx import nx +import networkx as nx from lfr.fig.interaction import InteractionType from lfr.netlistgenerator.mappinglibrary import Primitive class MappingOption: - def __init__(self, primitive: Primitive = None, subgraph_view=None) -> None: - + def __init__( + self, primitive: Optional[Primitive] = None, subgraph_view=None + ) -> None: self._primitive: Optional[Primitive] = primitive - self.fig_subgraph: nx.DiGraph = subgraph_view + self.fig_subgraph: Optional[nx.DiGraph] = subgraph_view # Figure out what computation needs to get done with this self._interaction_type: Optional[InteractionType] = None diff --git a/lfr/netlistgenerator/namegenerator.py b/lfr/netlistgenerator/namegenerator.py index 82abe8e..4b4aec5 100644 --- a/lfr/netlistgenerator/namegenerator.py +++ b/lfr/netlistgenerator/namegenerator.py @@ -1,5 +1,7 @@ from typing import Dict -from pymint import MINTComponent, MINTConnection, MINTDevice + +from parchmint import Component, Connection +from pymint.mintdevice import MINTDevice class NameGenerator: @@ -14,7 +16,7 @@ class NameGenerator: """ def __init__(self) -> None: - self._counter_dictionary = {} + self._counter_dictionary: Dict[str, int] = {} # Key - Old NAme, Value - new name self._cn_rename_map: Dict[str, Dict[str, str]] = {} self._rename_map: Dict[str, str] = {} @@ -42,7 +44,7 @@ def generate_name(self, technology_string: str) -> str: self._counter_dictionary[technology_string] = 1 return "{}_{}".format(technology_string, 1).lower().replace(" ", "_") - def rename_component(self, component: MINTComponent) -> str: + def rename_component(self, component: Component) -> str: """Renames the component Renames the component and stores the old name->new name reference in @@ -51,7 +53,7 @@ def rename_component(self, component: MINTComponent) -> str: NOTE - Renames the ID of the component too Args: - component (MINTComponent): Component we want to rename + component (Component): Component we want to rename Returns: str: Returns the new name for the component @@ -60,10 +62,10 @@ def rename_component(self, component: MINTComponent) -> str: new_name = self.generate_name(component.entity) self._rename_map[old_name] = new_name component.name = new_name - component.overwrite_id(new_name) + component.ID = new_name return new_name - def rename_cn_component(self, cn_id: str, component: MINTComponent) -> str: + def rename_cn_component(self, cn_id: str, component: Component) -> str: """Renames the Construction Node related component Also stores what the corresponding rename map against the construction @@ -71,7 +73,7 @@ def rename_cn_component(self, cn_id: str, component: MINTComponent) -> str: Args: cn_id (str): ConstructionNode ID - component (MINTComponent): Component we want to rename + component (Component): Component we want to rename Returns: str: New name of the component @@ -81,10 +83,10 @@ def rename_cn_component(self, cn_id: str, component: MINTComponent) -> str: # self._rename_map[old_name] = new_name self.store_cn_name(cn_id, old_name, new_name) component.name = new_name - component.overwrite_id(new_name) + component.ID = new_name return new_name - def rename_connection(self, connection: MINTConnection) -> str: + def rename_connection(self, connection: Connection) -> str: """Renames the connection Also renames the name of the components in the source/sink(s) @@ -93,7 +95,7 @@ def rename_connection(self, connection: MINTConnection) -> str: Keeps track of the rename in internal datastruction Args: - connection (MINTConnection): Connection we want to rename + connection (Connection): Connection we want to rename Returns: str: New name of the connection @@ -106,8 +108,11 @@ def rename_connection(self, connection: MINTConnection) -> str: new_name = self.generate_name(connection.entity) self._rename_map[old_name] = new_name connection.name = new_name - connection.overwrite_id(new_name) + connection.ID = new_name + # Check if Source is none + if connection.source is None: + raise ValueError("Source of connection {} is None".format(connection.ID)) # Rename source connection.source.component = self._rename_map[connection.source.component] @@ -117,7 +122,7 @@ def rename_connection(self, connection: MINTConnection) -> str: return new_name - def rename_cn_connection(self, cn_id: str, connection: MINTConnection) -> str: + def rename_cn_connection(self, cn_id: str, connection: Connection) -> str: """Renames connection with reference to construciton node Uses the internal data struction to save the @@ -125,7 +130,7 @@ def rename_cn_connection(self, cn_id: str, connection: MINTConnection) -> str: Args: cn_id (str): ConstructionNode ID - connection (MINTConnection): Connection we need to rename + connection (Connection): Connection we need to rename Returns: str: New name of the connection @@ -139,8 +144,11 @@ def rename_cn_connection(self, cn_id: str, connection: MINTConnection) -> str: # self._rename_map[old_name] = new_name self.store_cn_name(cn_id, old_name, new_name) connection.name = new_name - connection.overwrite_id(new_name) + connection.ID = new_name + # Check if Source is none + if connection.source is None: + raise ValueError("Source of connection {} is None".format(connection.ID)) # Rename source connection.source.component = self.get_cn_name( cn_id, connection.source.component @@ -152,7 +160,7 @@ def rename_cn_connection(self, cn_id: str, connection: MINTConnection) -> str: return new_name - def rename_netlist(self, cn_id: str, device: MINTDevice) -> None: + def rename_netlist(self, cn_id: str, mint_device: MINTDevice) -> None: """Renames the entire netlist corresponding to a ConstructionNode Calls upon all the different rename component, connection @@ -162,10 +170,10 @@ def rename_netlist(self, cn_id: str, device: MINTDevice) -> None: cn_id (str): ConstructionNode Id device (MINTDevice): Device we want to rename """ - for component in device.components: + for component in mint_device.device.components: self.rename_cn_component(cn_id, component) - for connection in device.connections: + for connection in mint_device.device.connections: self.rename_cn_connection(cn_id, connection) def store_name(self, old_name: str, new_name: str) -> None: diff --git a/lfr/netlistgenerator/netlist_generation.py b/lfr/netlistgenerator/netlist_generation.py new file mode 100644 index 0000000..5742f01 --- /dev/null +++ b/lfr/netlistgenerator/netlist_generation.py @@ -0,0 +1,161 @@ +from typing import Dict, List + +import networkx as nx +from parchmint import Target +from parchmint.connection import Connection +from pymint.mintdevice import MINTDevice + +from lfr.netlistgenerator.connectingoption import ConnectingOption +from lfr.netlistgenerator.constructiongraph.constructiongraph import ConstructionGraph +from lfr.netlistgenerator.mappinglibrary import MappingLibrary +from lfr.netlistgenerator.namegenerator import NameGenerator +from lfr.netlistgenerator.primitive import PrimitiveType + + +def generate_device( + construction_graph: ConstructionGraph, + scaffhold_device: MINTDevice, + name_generator: NameGenerator, + mapping_library: MappingLibrary, +) -> None: + # TODO - Generate the device + # Step 1 - go though each of the construction nodes and genrate the corresponding + # components + # Step 2 - generate the connections between the outputs to input on the connected + # construction nodes + # Step 3 - TODO - Generate the control network + + cn_component_mapping: Dict[str, List[str]] = {} + + node_ids = nx.dfs_preorder_nodes(construction_graph) + print("Nodes to Traverse:", node_ids) + + # Go through the ordered nodes and start creating the components + for node_id in node_ids: + cn = construction_graph.get_construction_node(node_id) + + # raise and error if the construction node has no primitive + if cn.primitive is None: + raise ValueError(f"Construction Node: {node_id} has no primitive") + + # Generate the netlist based on the primitive type + if cn.primitive.type is PrimitiveType.COMPONENT: + # Generate the component + component = cn.primitive.get_default_component( + name_generator, scaffhold_device.device.layers[0] + ) + + # Add to the scaffhold device + scaffhold_device.device.add_component(component) + + # Add to the component mapping + cn_component_mapping[node_id] = [component.ID] + + elif cn.primitive.type is PrimitiveType.NETLIST: + netlist = cn.primitive.get_default_netlist(cn.ID, name_generator) + + # Merge the netlist into the scaffhold device + scaffhold_device.device.merge_netlist(netlist) + + # Add to the component mapping + cn_component_mapping[node_id] = [ + component.ID for component in netlist.components + ] + + # Go through the edges and connect the components using the inputs and outputs of + # the primitives + for source_cn_id, target_cn_id in construction_graph.edges: + source_cn = construction_graph.get_construction_node(source_cn_id) + target_cn = construction_graph.get_construction_node(target_cn_id) + + # Get the output ConnectingOptions of the source cn + output_options = source_cn.output_options.copy() + input_options = target_cn.input_options.copy() + + # Pop and make a connection between the output and the input + source_option = output_options.pop() + target_option = input_options.pop() + + # Generate the target from the source option + source_targets = get_targets( + source_option, source_cn_id, name_generator, cn_component_mapping + ) + target_targets = get_targets( + target_option, target_cn_id, name_generator, cn_component_mapping + ) + + # If there is 1 source and 1 target, then connect the components + if len(source_targets) == 1 and len(target_targets) == 1: + create_device_connection( + source_targets.pop(), + target_targets.pop(), + name_generator, + scaffhold_device, + mapping_library, + ) + + elif len(source_targets) == len(target_targets): + raise NotImplementedError("Bus targets not implemented") + elif len(source_targets) == 1 and len(target_targets) > 1: + raise NotImplementedError("Multiple targets not implemented") + elif len(source_targets) > 1 and len(target_targets) == 1: + raise NotImplementedError("Multiple sources not implemented") + + +def create_device_connection( + source_target: Target, + target_target: Target, + name_generator: NameGenerator, + scaffhold_device: MINTDevice, + mapping_library: MappingLibrary, +) -> None: + # TODO - Create the connection based on parameters from the connecting option + # Step 1 - Get the connection from the mapping library + # TODO: Create new method stubs to get the right connection primitives from the + # mapping library (this would need extra criteria that that will need evaulation + # in the future (RAMA Extension)) + primitive = mapping_library.get_default_connection_entry() + # Step 2 - Create the connection in the device + connection_name = name_generator.generate_name(primitive.mint) + connection = Connection( + name=connection_name, + ID=connection_name, + entity=primitive.mint, + source=source_target, + sinks=[target_target], + layer=scaffhold_device.device.layers[ + 0 + ], # TODO - This will be replaced in the future when we introduce layer sharding + ) + scaffhold_device.device.add_connection(connection) + + +def get_targets( + option: ConnectingOption, + connection_node_id: str, + name_generator: NameGenerator, + cn_name_map, +) -> List[Target]: + ret: List[Target] = [] + + if option.component_name is None: + # TODO: Clarify the logic for doing this later on and put it in the docstring + component_names = cn_name_map[connection_node_id] + else: + # TODO: Clarify the logic for doing this later on and put it in the docstring + old_name = option.component_name + component_name = name_generator.get_cn_name(connection_node_id, old_name) + component_names = [component_name] + + for component_name in component_names: + for port_name in option.component_port: + # Check and make sure that the component name is valid + if component_name is None: + raise ValueError( + "Could not generate connection target for construction node" + f" {connection_node_id} since Port name is None" + ) + target = Target(component_name, port_name) + ret.append(target) + + return ret diff --git a/lfr/netlistgenerator/netlistsizor.py b/lfr/netlistgenerator/netlistsizor.py index fb795bc..040c027 100644 --- a/lfr/netlistgenerator/netlistsizor.py +++ b/lfr/netlistgenerator/netlistsizor.py @@ -1,4 +1,5 @@ from lfr.fig.interaction import InteractionType +from lfr.postprocessor.constraints import ConstraintList class NetlistSizor: diff --git a/lfr/netlistgenerator/networkmappingoption.py b/lfr/netlistgenerator/networkmappingoption.py index c3de245..6072450 100644 --- a/lfr/netlistgenerator/networkmappingoption.py +++ b/lfr/netlistgenerator/networkmappingoption.py @@ -1,7 +1,7 @@ from enum import Enum -from lfr.netlistgenerator.primitive import NetworkPrimitive from lfr.netlistgenerator.mappingoption import MappingOption +from lfr.netlistgenerator.primitive import NetworkPrimitive class NetworkMappingOptionType(Enum): diff --git a/lfr/netlistgenerator/packaged_libraries/dropx.py b/lfr/netlistgenerator/packaged_libraries/dropx.py index 30368b1..128eb87 100644 --- a/lfr/netlistgenerator/packaged_libraries/dropx.py +++ b/lfr/netlistgenerator/packaged_libraries/dropx.py @@ -1,11 +1,10 @@ from lfr.fig.interaction import InteractionType +from lfr.netlistgenerator.connectingoption import ConnectingOption from lfr.netlistgenerator.mappinglibrary import MappingLibrary from lfr.netlistgenerator.primitive import Primitive, PrimitiveType -from lfr.netlistgenerator.connectingoption import ConnectingOption def generate_dropx_library() -> MappingLibrary: - library = MappingLibrary("dropX") # # PORT diff --git a/lfr/netlistgenerator/primitive.py b/lfr/netlistgenerator/primitive.py index ccbbb58..8103f9d 100644 --- a/lfr/netlistgenerator/primitive.py +++ b/lfr/netlistgenerator/primitive.py @@ -1,15 +1,14 @@ import copy +import hashlib from enum import Enum from typing import List, Optional -from pymint.mintcomponent import MINTComponent +from parchmint import Component, Device, Layer, Params from pymint.mintdevice import MINTDevice -from pymint.mintlayer import MINTLayer -from pymint.mintparams import MINTParams -from lfr.netlistgenerator.gen_strategies.genstrategy import GenStrategy -from lfr.netlistgenerator.connectingoption import ConnectingOption from lfr import parameters +from lfr.netlistgenerator.connectingoption import ConnectingOption +from lfr.netlistgenerator.gen_strategies.genstrategy import GenStrategy from lfr.netlistgenerator.namegenerator import NameGenerator @@ -17,6 +16,7 @@ class PrimitiveType(Enum): COMPONENT = 0 NETLIST = 1 PROCEDURAL = 2 + CONNECTION = 3 class Primitive: @@ -27,14 +27,14 @@ def __init__( match_string: str = "", is_storage: bool = False, has_storage_control: bool = False, - inputs: List[ConnectingOption] = None, - outputs: List[ConnectingOption] = None, + inputs: Optional[List[ConnectingOption]] = None, + outputs: Optional[List[ConnectingOption]] = None, loadings: Optional[List[ConnectingOption]] = None, carriers: Optional[List[ConnectingOption]] = None, default_netlist: Optional[str] = None, - functional_input_params: List[str] = None, - output_params: List[str] = None, - user_defined_params: MINTParams = MINTParams({}), + functional_input_params: Optional[List[str]] = None, + output_params: Optional[List[str]] = None, + user_defined_params: Params = Params({}), ) -> None: if inputs is None: inputs = [] @@ -69,7 +69,16 @@ def __init__( self._functional_input_params = functional_input_params self._output_params = output_params - self._user_defined_params: MINTParams = user_defined_params + self._user_defined_params: Params = user_defined_params + + # Generate the UID for the primitive + self._uid = hashlib.md5( + f"{self._mint}_{self._match_string}".encode("utf-8") + ).hexdigest() + + @property + def uid(self) -> str: + return self._uid @property def type(self) -> PrimitiveType: @@ -84,17 +93,33 @@ def match_string(self) -> str: return self._match_string def export_inputs(self, subgraph) -> List[ConnectingOption]: + # TODO - Figure out how to map connecting options to match string nodes + print( + "Warning: Implment how the connecting option is mapped to match string node" + ) return [copy.copy(c) for c in self._inputs] def export_outputs(self, subgraph) -> List[ConnectingOption]: + # TODO - Figure out how to map connecting options to match string nodes + print( + "Warning: Implment how the connecting option is mapped to match string node" + ) return [copy.copy(c) for c in self._outputs] def export_loadings(self, subgraph) -> Optional[List[ConnectingOption]]: + # TODO - Figure out how to map connecting options to match string nodes + print( + "Warning: Implment how the connecting option is mapped to match string node" + ) if self._loadings is None: return None return [copy.copy(c) for c in self._loadings] def export_carriers(self, subgraph) -> Optional[List[ConnectingOption]]: + # TODO - Figure out how to map connecting options to match string nodes + print( + "Warning: Implment how the connecting option is mapped to match string node" + ) if self._carriers is None: return None return [copy.copy(c) for c in self._carriers] @@ -111,9 +136,7 @@ def inverse_design_query_params(self): def output_params(self): return self._output_params - def get_default_component( - self, name_gen: NameGenerator, layer: MINTLayer - ) -> MINTComponent: + def get_default_component(self, name_gen: NameGenerator, layer: Layer) -> Component: """Gets the default component for the primitive Utilizes the NameGenerator instance to generate a new component instance of @@ -122,21 +145,23 @@ def get_default_component( Args: name_gen (NameGenerator): NameGenerator instance that will generate the new name for the component - layer (MINTLayer): Layer object in which the component exists + layer (Layer): Layer object in which the component exists Raises: Exception: Raises an exception when the entry is not of the type COMPONENT Returns: - MINTComponent: New component object + Component: New component object """ if self.type is not PrimitiveType.COMPONENT: raise Exception("Cannot execute this method for this kind of a primitive") name = name_gen.generate_name(self.mint) - mc = MINTComponent(name, self.mint, {}, [layer]) + mc = Component( + name=name, ID=name, entity=self.mint, params=Params({}), layers=[layer] + ) return mc - def get_default_netlist(self, cn_id: str, name_gen: NameGenerator) -> MINTDevice: + def get_default_netlist(self, cn_id: str, name_gen: NameGenerator) -> Device: """Returns the default netlist for the primitive Args: @@ -157,17 +182,20 @@ def get_default_netlist(self, cn_id: str, name_gen: NameGenerator) -> MINTDevice ) default_mint_file = parameters.LIB_DIR.joinpath(self._default_netlist).resolve() - device = MINTDevice.from_mint_file(str(default_mint_file)) + mint_device = MINTDevice.from_mint_file(str(default_mint_file)) - if device is None: + if mint_device is None: raise Exception( "Unable to parse MINT file: {} for construction node {}".format( str(default_mint_file), cn_id ) ) - name_gen.rename_netlist(cn_id, device) + name_gen.rename_netlist(cn_id, mint_device) # Return the default netlist - return device + return mint_device.device + + def __hash__(self) -> int: + return hash("{}_{}".format(self.mint, self.match_string)) class ProceduralPrimitive(Primitive): @@ -184,7 +212,7 @@ def __init__( default_netlist: Optional[str], functional_input_params: List[str], output_params: List[str], - user_defined_params: MINTParams, + user_defined_params: Params = Params({}), ) -> None: super().__init__( mint=mint, @@ -214,33 +242,27 @@ def export_loadings(self, subgraph) -> Optional[List[ConnectingOption]]: def export_carriers(self, subgraph) -> Optional[List[ConnectingOption]]: raise NotImplementedError() - def get_default_component( - self, name_gen: NameGenerator, layer: MINTLayer - ) -> MINTComponent: + def get_default_component(self, name_gen: NameGenerator, layer: Layer) -> Component: raise NotImplementedError() def get_procedural_component( - self, name_gen: NameGenerator, layer: MINTLayer - ) -> MINTComponent: + self, name_gen: NameGenerator, layer: Layer + ) -> Component: raise NotImplementedError() def generate_input_connectingoptions(self, subgraph_view) -> List[ConnectingOption]: """Generates a list of connection options that represent where the inputs can - be connected to the primitive + be connected to the primitive - Args: - subgraph_view (networkx.Graph.subgraph): A subgraph view of the Fluid - <<<<<<< HEAD - Interaction Graph - ======= - Interaction Graph - >>>>>>> Updating the code to reduce problems from pylance + Args: + subgraph_view (networkx.Graph.subgraph): A subgraph view of the Fluid + Interaction Graph - Raises: - NotImplementedError: Raised when its not implemented + Raises: + NotImplementedError: Raised when its not implemented - Returns: - List[ConnectingOption]: List of options where we can attach connections + Returns: + List[ConnectingOption]: List of options where we can attach connections """ raise NotImplementedError() @@ -248,22 +270,17 @@ def generate_output_connectingoptions( self, subgraph_view ) -> List[ConnectingOption]: """Generates a list of connection options that represent where the outputs can - be connected to the primitive - - Args: - subgraph_view (networkx.Graph.subgraph): A subgraph view of the Fluid - <<<<<<< HEAD - Interaction Graph - ======= - Interaction Graph - >>>>>>> Updating the code to reduce problems from pylance + be connected to the primitive + Args: + subgraph_view (networkx.Graph.subgraph): A subgraph view of the Fluid + Interaction Graph - Raises: - NotImplementedError: Raised when its not implemented + Raises: + NotImplementedError: Raised when its not implemented - Returns: - List[ConnectingOption]: List of options where we can attach connections + Returns: + List[ConnectingOption]: List of options where we can attach connections """ raise NotImplementedError() @@ -271,21 +288,17 @@ def generate_carrier_connectingoptions( self, subgraph_view ) -> List[ConnectingOption]: """Generates a list of connection options that represent where the carrier inputs can - be connected to the primitive + be connected to the primitive - Args: - subgraph_view (networkx.Graph.subgraph): A subgraph view of the Fluid - <<<<<<< HEAD - Interaction Graph - ======= - Interaction Graph - >>>>>>> Updating the code to reduce problems from pylance + Args: + subgraph_view (networkx.Graph.subgraph): A subgraph view of the Fluid + Interaction Graph - Raises: - NotImplementedError: Raised when its not implemented + Raises: + NotImplementedError: Raised when its not implemented - Returns: - List[ConnectingOption]: List of options where we can attach connections + Returns: + List[ConnectingOption]: List of options where we can attach connections """ raise NotImplementedError() @@ -293,21 +306,17 @@ def generate_loading_connectingoptions( self, subgraph_view ) -> List[ConnectingOption]: """Generates a list of connection options that represent where the loading inputs can - be connected to the primitive + be connected to the primitive - Args: - subgraph_view (networkx.Graph.subgraph): A subgraph view of the Fluid - <<<<<<< HEAD - Interaction Graph - ======= - Interaction Graph - >>>>>>> Updating the code to reduce problems from pylance + Args: + subgraph_view (networkx.Graph.subgraph): A subgraph view of the Fluid + Interaction Graph - Raises: - NotImplementedError: Raised when its not implemented + Raises: + NotImplementedError: Raised when its not implemented - Returns: - List[ConnectingOption]: List of options where we can attach connections + Returns: + List[ConnectingOption]: List of options where we can attach connections """ raise NotImplementedError() @@ -330,14 +339,14 @@ def __init__(self, fig_subgraph_view, gen_strategy: GenStrategy) -> None: # Write methods that will utilize the subgraph view to generate the # netlist self._fig_subgraph_view = fig_subgraph_view - self._netlist: Optional[MINTDevice] = None + self._default_netlist: Optional[MINTDevice] = None def generate_netlist(self) -> None: """Generates the netlist for the given network primitive, this method generates the flow network, input , output, carriers and loadings into the primitve properties """ - self._netlist = self._gen_strategy.generate_flow_network( + self._default_netlist = self._gen_strategy.generate_flow_network( self._fig_subgraph_view ) self._inputs = self._gen_strategy.generate_input_connectingoptions( @@ -353,31 +362,25 @@ def generate_netlist(self) -> None: self._fig_subgraph_view ) - def get_default_netlist(self, cn_id: str, name_gen: NameGenerator) -> MINTDevice: + def get_default_netlist(self, cn_id: str, name_gen: NameGenerator) -> Device: """Returns the default netlist for the primitive - Args: - cn_id (str): ID of the construction node so that we can prefix the id's of - <<<<<<< HEAD - all the components that are part of the default netlist - name_gen (NameGenerator): A namegenerator instance that is used for the - globally for synthesizing the design - ======= - all the components that are part of the default netlist - name_gen (NameGenerator): A namegenerator instance that is used for the - globally for synthesizing the design - >>>>>>> Updating the code to reduce problems from pylance - - Raises: - Exception: Raised when there is no defualt netlist is generated - - Returns: - MINTDevice: Default netlist of whatever the primitive is + Args: + cn_id (str): ID of the construction node so that we can prefix the id's of + all the components that are part of the default netlist + name_gen (NameGenerator): A namegenerator instance that is used for the + globally for synthesizing the design + + Raises: + Exception: Raised when there is no defualt netlist is generated + + Returns: + MINTDevice: Default netlist of whatever the primitive is """ - if self._netlist is None: + if self._default_netlist is None: raise Exception("No default netlist present for the primitive") # Utilise the subgraph view to decide how you want to generate a netlist # Load all the inputs and outputs based on that information - name_gen.rename_netlist(cn_id, self._netlist) - return self._netlist + name_gen.rename_netlist(cn_id, self._default_netlist) + return self._default_netlist.device diff --git a/lfr/netlistgenerator/procedural_component_algorithms/gradient_generator.py b/lfr/netlistgenerator/procedural_component_algorithms/gradient_generator.py index 939aa83..6ede9ed 100644 --- a/lfr/netlistgenerator/procedural_component_algorithms/gradient_generator.py +++ b/lfr/netlistgenerator/procedural_component_algorithms/gradient_generator.py @@ -1,6 +1,6 @@ -from lfr.netlistgenerator.connectingoption import ConnectingOption from typing import List +from lfr.netlistgenerator.connectingoption import ConnectingOption from lfr.netlistgenerator.primitive import ProceduralPrimitive diff --git a/lfr/netlistgenerator/procedural_component_algorithms/mux.py b/lfr/netlistgenerator/procedural_component_algorithms/mux.py index f748022..cde9349 100644 --- a/lfr/netlistgenerator/procedural_component_algorithms/mux.py +++ b/lfr/netlistgenerator/procedural_component_algorithms/mux.py @@ -1,6 +1,6 @@ -from lfr.netlistgenerator.connectingoption import ConnectingOption from typing import List +from lfr.netlistgenerator.connectingoption import ConnectingOption from lfr.netlistgenerator.primitive import ProceduralPrimitive diff --git a/lfr/netlistgenerator/procedural_component_algorithms/mux3d.py b/lfr/netlistgenerator/procedural_component_algorithms/mux3d.py index 9f3d9c0..dd022f1 100644 --- a/lfr/netlistgenerator/procedural_component_algorithms/mux3d.py +++ b/lfr/netlistgenerator/procedural_component_algorithms/mux3d.py @@ -1,6 +1,6 @@ -from lfr.netlistgenerator.connectingoption import ConnectingOption from typing import List +from lfr.netlistgenerator.connectingoption import ConnectingOption from lfr.netlistgenerator.primitive import ProceduralPrimitive diff --git a/lfr/netlistgenerator/procedural_component_algorithms/transposer.py b/lfr/netlistgenerator/procedural_component_algorithms/transposer.py index 8080d48..fe37c1a 100644 --- a/lfr/netlistgenerator/procedural_component_algorithms/transposer.py +++ b/lfr/netlistgenerator/procedural_component_algorithms/transposer.py @@ -1,6 +1,6 @@ -from lfr.netlistgenerator.connectingoption import ConnectingOption from typing import List +from lfr.netlistgenerator.connectingoption import ConnectingOption from lfr.netlistgenerator.primitive import ProceduralPrimitive diff --git a/lfr/netlistgenerator/procedural_component_algorithms/tree.py b/lfr/netlistgenerator/procedural_component_algorithms/tree.py index ec898a1..0e5dc5d 100644 --- a/lfr/netlistgenerator/procedural_component_algorithms/tree.py +++ b/lfr/netlistgenerator/procedural_component_algorithms/tree.py @@ -1,6 +1,6 @@ -from lfr.netlistgenerator.connectingoption import ConnectingOption from typing import List +from lfr.netlistgenerator.connectingoption import ConnectingOption from lfr.netlistgenerator.primitive import ProceduralPrimitive diff --git a/lfr/netlistgenerator/procedural_component_algorithms/ytree.py b/lfr/netlistgenerator/procedural_component_algorithms/ytree.py index d31698c..3009e97 100644 --- a/lfr/netlistgenerator/procedural_component_algorithms/ytree.py +++ b/lfr/netlistgenerator/procedural_component_algorithms/ytree.py @@ -1,8 +1,8 @@ -from lfr.netlistgenerator.connectingoption import ConnectingOption from typing import List, Optional -from pymint import MINTComponent, MINTLayer +from parchmint import Component, Layer, Params +from lfr.netlistgenerator.connectingoption import ConnectingOption from lfr.netlistgenerator.namegenerator import NameGenerator from lfr.netlistgenerator.primitive import ProceduralPrimitive @@ -62,8 +62,8 @@ def export_carriers(self, subgraph) -> Optional[List[ConnectingOption]]: return None def get_procedural_component( - self, name_gen: NameGenerator, layer: MINTLayer, subgraph - ) -> MINTComponent: + self, name_gen: NameGenerator, layer: Layer, subgraph + ) -> Component: name = name_gen.generate_name(self.mint) params = {} # Calculate param values based on the subgraph @@ -77,7 +77,9 @@ def get_procedural_component( params["width"] = 5 params["height"] = 5 params["stageLength"] = 5 - mc = MINTComponent(name, self.mint, params, [layer]) + mc = Component( + ID=name, name=name, entity=self.mint, params=Params(params), layers=[layer] + ) return mc def generate_input_connectingoptions(self, subgraph_view) -> List[ConnectingOption]: diff --git a/lfr/notes.md b/lfr/notes.md new file mode 100644 index 0000000..24351f1 --- /dev/null +++ b/lfr/notes.md @@ -0,0 +1,29 @@ +# Gen Strategys: + + +## DropX Flow Validation Rules: + +### Rule 1 - + +The first level of % should be mapping to a Droplet Generator + +### Rule 2 – + +Any +-, distribute nodes before % should be in continuous flow + +### (Incorrect) Rule 3 - + +Any Remetering (%) should require a droplet breakdown and regeneration + +### Rule 4 – + +Distribute network post Metering stage should be mapped to different kinds of separator / selection/ storage networks + +### Rule 5 – + +If plus is shown between node that has % in pred and non % in pred, then its pico injection + +### Rule 6 – + +If plus is sown between two nodes that has % in pred, then its droplet merging + diff --git a/lfr/parameters.py b/lfr/parameters.py index 9e414fa..e365e24 100644 --- a/lfr/parameters.py +++ b/lfr/parameters.py @@ -6,3 +6,4 @@ LFR_DIR = pathlib.Path(lfr.__file__).parent.parent.absolute() LIB_DIR = LFR_DIR.joinpath("library") OUTPUT_DIR = LFR_DIR.joinpath("output") +PREPROCESSOR_DUMP_FILE_NAME = "pre_processor_dump.lfr" diff --git a/lfr/postProcessListener.py b/lfr/postProcessListener.py index 3c9c4cf..49b2726 100644 --- a/lfr/postProcessListener.py +++ b/lfr/postProcessListener.py @@ -1,5 +1,6 @@ from typing import Dict, List +from lfr.antlrgen.lfr.lfrXParser import lfrXParser from lfr.fig.fignode import FIGNode from lfr.fig.interaction import FluidProcessInteraction, Interaction from lfr.moduleinstanceListener import ModuleInstanceListener @@ -11,7 +12,6 @@ PumpMapping, StorageMapping, ) -from lfr.antlrgen.lfr.lfrXParser import lfrXParser class PostProcessListener(ModuleInstanceListener): @@ -24,7 +24,8 @@ def __init__(self) -> None: def enterPerformancedirective(self, ctx: lfrXParser.PerformancedirectiveContext): super().enterPerformancedirective(ctx) # TODO - Make a list of all the nodes previous - fig = self.currentModule.FIG + # TODO - Check is this needs to be utilized in the future + # fig = self.currentModule.FIG # Update the previous list of nodes self.__make_prev_fig_nodes_list() @@ -40,7 +41,7 @@ def enterPerformancedirective(self, ctx: lfrXParser.PerformancedirectiveContext) # mapping.operator = operator self._current_mappings[operator] = mapping - for constraint in ctx.constraint(): + for constraint in ctx.constraint(): # type: ignore param_name = constraint.ID().getText() conditional_operator = constraint.operator.text value = float(constraint.number().getText()) @@ -145,20 +146,20 @@ def exitAssignstat(self, ctx: lfrXParser.AssignstatContext): # to the fig (get the inputs and output nodes from the LHS and RHS) if "assign" in self._current_mappings.keys(): # Get the LHS and RHS nodes here - lhs = self._lhs_store - rhs = self._rhs_store + lhs: List[FIGNode] = self._lhs_store + rhs: List[FIGNode] = self._rhs_store # Check if there is an `assign` mapping if "assign" in self._current_mappings.keys(): mapping = self._current_mappings["assign"] # Now add the LHS and RHS nodes into the mapping - mapping_instance = NetworkMapping() + network_mapping_instance = NetworkMapping() for node in rhs: - mapping_instance.input_nodes.append(node) + network_mapping_instance.input_nodes.append(node) for node in lhs: - mapping_instance.output_nodes.append(node) + network_mapping_instance.output_nodes.append(node) - mapping.instances.append(mapping_instance) + mapping.instances.append(network_mapping_instance) # TODO - Go through the `nodes_of_interest` and then check to see # if any of the nodes have the corresponding mappings in the cache @@ -178,6 +179,8 @@ def exitAssignstat(self, ctx: lfrXParser.AssignstatContext): self.__clear_mappings() def __make_prev_fig_nodes_list(self): + if self.currentModule is None: + raise ValueError("No module found") fig = self.currentModule.FIG self._prev_node_list = [] self._after_node_list = [] @@ -185,6 +188,8 @@ def __make_prev_fig_nodes_list(self): self._prev_node_list.append(node) def __find_new_fig_nodes(self) -> List[FIGNode]: + if self.currentModule is None: + raise ValueError("No module found") fig = self.currentModule.FIG for node in fig.nodes: self._after_node_list.append(node) @@ -196,5 +201,7 @@ def __find_new_fig_nodes(self) -> List[FIGNode]: return [fig.get_fignode(n) for n in nodes_of_interest] def __clear_mappings(self) -> None: + if self.currentModule is None: + raise ValueError("No module found") self.currentModule.mappings.extend(self._current_mappings.values()) self._current_mappings.clear() diff --git a/lfr/postprocessor/constraints.py b/lfr/postprocessor/constraints.py index 6f68218..1838f44 100644 --- a/lfr/postprocessor/constraints.py +++ b/lfr/postprocessor/constraints.py @@ -1,6 +1,7 @@ +from enum import Enum from typing import List, Optional -from pymint import MINTComponent +from parchmint import Component """ # TODO - Generate the constraints in the right way: @@ -36,54 +37,78 @@ """ +class ConstriantType(Enum): + MIN = "MIN" + MAX = "MAX" + TARGET = "TARGET" + + class Constraint: + """Base Constraints Class that accepts the different + kinds of constraints that will be used throughout the + postprocessing. + + Individual constraints are currently not uniquely identifyable + Generate the constraint and set the corresponding min max target values + + TODO - Maybe simplify the interfacet to automatically check the constraint + type while retrieving data + """ + def __init__(self) -> None: - self.__constraint_key = None + # This will always be something + self._constraint_key = "" + + # Constraint Type that we will use in the future + self._constraint_type: ConstriantType = ConstriantType.TARGET # Store here if its '=' # Also store here when its '<=' and '>=' - self.__target_value = None + self._target_value: Optional[float] = None # Store here if its '>' - self.__min_value = None + self._min_value: Optional[float] = None # Store here if its '<' - self.__max_value = None + self._max_value: Optional[float] = None - self.__unit_string: Optional[str] = None + self._unit_string: Optional[str] = None @property def unit(self) -> Optional[str]: - return self.__unit_string + return self._unit_string @unit.setter def unit(self, value: str) -> None: - self.__unit_string = value + self._unit_string = value @property def key(self): - return self.__constraint_key + return self._constraint_key def add_target_value(self, key: str, value: float) -> None: - self.__target_value = value - self.__constraint_key = key + self._constraint_type = ConstriantType.TARGET + self._target_value = value + self._constraint_key = key - def get_target_value(self): - return self.__target_value + def get_target_value(self) -> Optional[float]: + return self._target_value def add_min_value(self, key: str, value: float) -> None: - self.__min_value = value - self.__constraint_key = key + self._constraint_type = ConstriantType.MIN + self._min_value = value + self._constraint_key = key - def get_min_value(self): - return self.__min_value + def get_min_value(self) -> Optional[float]: + return self._min_value def add_max_value(self, key: str, value: float) -> None: - self.__max_value = value - self.__constraint_key = key + self._constraint_type = ConstriantType.MAX + self._max_value = value + self._constraint_key = key - def get_max_value(self): - return self.__max_value + def get_max_value(self) -> Optional[float]: + return self._max_value class PerformanceConstraint(Constraint): @@ -102,22 +127,31 @@ def __init__(self) -> None: class MaterialConstraint(Constraint): - def __init__(self): + def __init__(self) -> None: super().__init__() class ConstraintList: - def __init__(self, component: MINTComponent): + """Stores the constraints for a specific component + + This is a mapping between a component and its constraints + """ + + def __init__(self, component: Component): + """Creates a new instance of a constraint list + + Args: + component (Component): Component against which we want to store the constraints + """ super().__init__() self.__constraints: List[Constraint] = [] - self.__component: Optional[MINTComponent] = component + self.__component: Optional[Component] = component def add_constraint(self, constraint: Constraint) -> None: - constraint = FunctionalConstraint() self.__constraints.append(constraint) @property - def component(self) -> Optional[MINTComponent]: + def component(self) -> Optional[Component]: return self.__component def __len__(self): diff --git a/lfr/preprocessor.py b/lfr/preprocessor.py index 44353b5..8da023f 100644 --- a/lfr/preprocessor.py +++ b/lfr/preprocessor.py @@ -9,14 +9,21 @@ from lfr.antlrgen.lfr.lfrXLexer import lfrXLexer from lfr.antlrgen.lfr.lfrXParser import lfrXParser +from lfr.parameters import PREPROCESSOR_DUMP_FILE_NAME IMPORT_FILE_PATTERN = r"(`import\s+\"(\w+.lfr)\")" class PreProcessor: def __init__(self, file_list: List[str], lib_dir_list: List[str] = []) -> None: - self.resolved_paths = {} - self.full_text = {} + """Instantiates a new instance of the preprocessor + + Args: + file_list (List[str]): List of files to be preprocessed + lib_dir_list (List[str], optional): Directory path for the library. Defaults to []. + """ + self.resolved_paths: Dict[str, Path] = {} + self.full_text: Dict[str, str] = {} self.text_dump = None self._lib_file_list: Dict[str, str] = {} # Stores file path to file @@ -33,7 +40,6 @@ def __init__(self, file_list: List[str], lib_dir_list: List[str] = []) -> None: self._lib_file_list[str(path_object.name)] = str(full_path) for file_path in file_list: - extension = Path(file_path).suffix if extension != ".lfr": print("Unrecognized file Extension") @@ -43,6 +49,11 @@ def __init__(self, file_list: List[str], lib_dir_list: List[str] = []) -> None: self.__store_full_text(p) def check_syntax_errors(self) -> bool: + """Checks if there are any syntax errors in the input files + + Returns: + bool: True if there are syntax errors, False otherwise + """ syntax_errors = 0 for file_path in list(self.resolved_paths.values()): print("File: {}".format(file_path)) @@ -59,8 +70,17 @@ def check_syntax_errors(self) -> bool: return syntax_errors > 0 - def process(self) -> None: + def process( + self, preprocesser_dump_path: Path = Path(f"./{PREPROCESSOR_DUMP_FILE_NAME}") + ) -> None: + """Processes the preprocessor and generates the preprocessor dump file + Args: + preprocesser_dump_path (Path, optional): Path for the preprocessor dump file. Defaults to Path(f"./{PREPROCESSOR_DUMP_FILE_NAME}"). + + Raises: + Exception: TBA + """ dep_graph = nx.DiGraph() # add the nodes in the dep graph for file_handle in self.full_text: @@ -79,15 +99,12 @@ def process(self) -> None: # Check if the file handle is found in the dependency graph if new_file_handle not in list(dep_graph.nodes): - # Since its not in the dependency graph we check if # its in the preloaded library if new_file_handle not in list(self._lib_file_list.keys()): - # Since its not in the preloaded library either... raise Exception("Could not find file - {}".format(result[1])) else: - # Pull all the text, add it to the full text store file_path = self._lib_file_list[new_file_handle] p = Path(file_path).resolve() @@ -113,7 +130,7 @@ def process(self) -> None: final_dump += "\n\n\n\n\n" # Generating the Dump - file = open("pre_processor_dump.lfr", "w") + file = open(preprocesser_dump_path, "w") file.write(final_dump) file.close() @@ -128,10 +145,10 @@ def __store_full_text(self, file_path: Path): file = open(file_path, mode="r") # read all lines at once - all_of_it = file.read() + all_of_the_file_text = file.read() # close the file file.close() self.resolved_paths[file_path.name] = file_path - self.full_text[file_path.name] = all_of_it + self.full_text[file_path.name] = all_of_the_file_text diff --git a/lfr/utils.py b/lfr/utils.py index 16837d4..1a4f3ec 100644 --- a/lfr/utils.py +++ b/lfr/utils.py @@ -1,42 +1,58 @@ import json import os - +from pathlib import Path from typing import List -from networkx import nx + +import networkx as nx from pymint.mintdevice import MINTDevice -import lfr.parameters as parameters +from lfr.parameters import OUTPUT_DIR -def printgraph(G, filename: str) -> None: +def printgraph(graph: nx.Graph, filename: str, output_dir: Path = OUTPUT_DIR) -> None: + """Prints the graph in a .dot file and a .pdf file + + Args: + graph (nx.Graph): graph we need to print + filename (str): name of the file + output_dir (Path, optional): Output folder path. Defaults to OUTPUT_DIR. + """ # Generate labels and whatnot for the graph - H = G.copy(as_view=False) + graph_copy = graph.copy(as_view=False) # Print out the dot file and then run the conversion - tt = os.path.join(parameters.OUTPUT_DIR, filename) - print("output:", parameters.OUTPUT_DIR) - print("output:", tt) - nx.nx_agraph.to_agraph(H).write(tt) + dot_path = Path.joinpath(output_dir, f"{filename}.dot") + pdf_path = Path.joinpath(output_dir, f"{filename}.pdf") + print("output:", output_dir) + print("output:", dot_path) + nx.nx_agraph.to_agraph(graph_copy).write(dot_path) - os.system("dot -Tpdf {} -o {}.pdf".format(tt, tt)) + os.system(f"dot -Tpdf {str(dot_path.absolute())} -o {str(pdf_path.absolute())}") def get_ouput_path(filename: str) -> str: - return os.path.join(parameters.OUTPUT_DIR, filename) + """Returns the path to the output file""" + return os.path.join(OUTPUT_DIR, filename) + +def serialize_netlist(output_path: Path, mint_device: MINTDevice) -> None: + """Serializes the netlist to a json file""" -def serialize_netlist(device: MINTDevice) -> None: # Generate the MINT file from the pyparchmint device - json_data = device.to_parchmint_v1() + json_data = mint_device.to_parchmint() json_string = json.dumps(json_data) - json_file = open(get_ouput_path(device.name + ".json"), "wt") + file_path = output_path.joinpath(f"{mint_device.device.name}.json") + json_file = open(file_path, "wt") json_file.write(json_string) json_file.close() -def print_netlist(device: MINTDevice) -> None: +def print_netlist(output_path: Path, mint_device: MINTDevice) -> None: + """Prints the netlist to the console""" + # Generate the MINT file from the pyparchmint device - minttext = device.to_MINT() - mint_file = open(get_ouput_path(device.name + ".mint"), "wt") + minttext = mint_device.to_MINT() + file_path = Path.joinpath(output_path, f"{mint_device.device.name}.mint") + mint_file = open(file_path, "wt") mint_file.write(minttext) mint_file.close() @@ -50,5 +66,5 @@ def convert_list_to_str(lst: List) -> str: Returns: str: list formatted as a string """ - ret = "[{}]".format(", ".join([str(i) for i in lst])) + ret = "[{0}]".format(", ".join([str(i) for i in lst])) return ret diff --git a/library/mars.json b/library/mars.json index 5e9ecc0..a4c2b0a 100644 --- a/library/mars.json +++ b/library/mars.json @@ -81,7 +81,7 @@ "default-netlist": null }, { - "mint": "DIAMOND CHAMBER", + "mint": "DIAMOND REACTION CHAMBER", "component-type": "primitive", "is-storage": "false", "inputs": [ diff --git a/lfr/netlistgenerator/constructiongraph.py b/old-stuff/constructiongraph-old.py similarity index 99% rename from lfr/netlistgenerator/constructiongraph.py rename to old-stuff/constructiongraph-old.py index 49e462d..44ba83e 100644 --- a/lfr/netlistgenerator/constructiongraph.py +++ b/old-stuff/constructiongraph-old.py @@ -1,7 +1,7 @@ from copy import copy from typing import Dict, List, Optional, Set, Tuple -from networkx import nx +import networkx as nx from networkx.algorithms import isomorphism from networkx.classes.digraph import DiGraph from pymint.mintcomponent import MINTComponent @@ -11,16 +11,16 @@ from lfr.compiler.module import Module from lfr.fig.fignode import FIGNode from lfr.fig.fluidinteractiongraph import FluidInteractionGraph -from lfr.netlistgenerator.namegenerator import NameGenerator -from lfr.netlistgenerator.primitive import PrimitiveType, ProceduralPrimitive from lfr.netlistgenerator.constructionnode import ConstructionNode +from lfr.netlistgenerator.namegenerator import NameGenerator from lfr.netlistgenerator.networkmappingoption import ( NetworkMappingOption, NetworkMappingOptionType, ) +from lfr.netlistgenerator.primitive import PrimitiveType, ProceduralPrimitive -class ConstructionGraph(nx.DiGraph): +class OLDConstructionGraph(nx.DiGraph): """Construction Graph is the proxy datastructure that we use for representing the loose connections between the fluid interaction graph and the real hardware design primitives that would be pieced together. @@ -602,7 +602,6 @@ def generate_edges(self, fig: FluidInteractionGraph) -> None: # combinatorial design space assert len(cn.mapping_options) == 1 for mapping_option in cn.mapping_options: - # TODO - Figure out how to not explicity check for this scenario' # right now I'm using component replace as a coarse way of ensure # no double takes @@ -620,7 +619,6 @@ def generate_edges(self, fig: FluidInteractionGraph) -> None: # continue for node_id in mapping_option.fig_subgraph.nodes: if node_id in fig_nodes_cn_reverse_map.keys(): - # Make sure there are no repeats here if cn.ID not in fig_nodes_cn_reverse_map[node_id]: fig_nodes_cn_reverse_map[node_id].append(cn.ID) diff --git a/lfr/netlistgenerator/constructionnode.py b/old-stuff/constructionnode-old.py similarity index 99% rename from lfr/netlistgenerator/constructionnode.py rename to old-stuff/constructionnode-old.py index 5cb5117..c935ff0 100644 --- a/lfr/netlistgenerator/constructionnode.py +++ b/old-stuff/constructionnode-old.py @@ -1,10 +1,11 @@ -from lfr.postprocessor.constraints import Constraint +from typing import List + from lfr.netlistgenerator.connectingoption import ConnectingOption from lfr.netlistgenerator.mappingoption import MappingOption -from typing import List +from lfr.postprocessor.constraints import Constraint -class ConstructionNode: +class OLDConstructionNode: def __init__(self, node_id: str) -> None: self._id = node_id self._mapping_options: List[MappingOption] = [] diff --git a/lfr/netlistgenerator/dafdadapter-old.py b/old-stuff/dafdadapter-old.py similarity index 99% rename from lfr/netlistgenerator/dafdadapter-old.py rename to old-stuff/dafdadapter-old.py index 1aab89f..9c9f90d 100644 --- a/lfr/netlistgenerator/dafdadapter-old.py +++ b/old-stuff/dafdadapter-old.py @@ -1,11 +1,12 @@ +from dafd import DAFD_Interface +from pymint.mintdevice import MINTDevice + from lfr.postprocessor.constraints import ( ConstraintList, FunctionalConstraint, GeometryConstraint, PerformanceConstraint, ) -from pymint.mintdevice import MINTDevice -from dafd import DAFD_Interface class DAFDSizingAdapter: diff --git a/old-stuff/dropxstrategy-old.py b/old-stuff/dropxstrategy-old.py new file mode 100644 index 0000000..d208322 --- /dev/null +++ b/old-stuff/dropxstrategy-old.py @@ -0,0 +1,296 @@ +# from lfr.netlistgenerator.constructiongraph import ConstructionGraph +from typing import List + +import networkx as nx +from pymint import MINTDevice + +from lfr.fig.fluidinteractiongraph import FluidInteractionGraph +from lfr.fig.interaction import Interaction, InteractionType +from lfr.netlistgenerator.constructiongraph.constructiongraph import ConstructionGraph +from lfr.netlistgenerator.constructiongraph.constructionnode import ConstructionNode +from lfr.netlistgenerator.dafdadapter import DAFDAdapter +from lfr.netlistgenerator.gen_strategies.genstrategy import GenStrategy + + +class DropXStrategy(GenStrategy): + def __init__(self, fig: FluidInteractionGraph) -> None: + super().__init__(fig) + + def reduce_mapping_options(self) -> None: + # Generate a topological order for the FIG to make sure athat we know the order of things + figs_in_order = list(nx.topological_sort(self._fig)) + # Generate the mapping between fignodes and construction nodes + # input_fignodes = self._fig.get_input_fignodes + for fignode_id in self._fig.nodes: + fignode = self._fig.get_fignode(fignode_id) + + # check if construction node + if ConstructionNode(fignode.ID).is_explictly_mapped: + pass + # TODO - Implement Generalized Ali Strategy 1 + # Rule 1 - The first level of % should be mapping to a Droplet Generator + # (TODO - Talk to ali about this or a T junction generator) + # Step 1 - Check if fig node is interaction and of type METER (%) this is t + # he check condition for rule 1 + else: + if isinstance(fignode, Interaction): + if fignode.type is InteractionType.METER: + is_first_metering_node = True + # TODO - Check if DROPLET GENERATOR is one of the mapping + # options, skip if not + # Step 2 - If type is meter then check to see if other it is + # the first meter operation from inputs to current fignode + for sorted_fignode_id in figs_in_order: + if fignode_id == sorted_fignode_id: + # End of checking + if is_first_metering_node is True: + # TODO - Eliminate all options other than Nozzle + # droplet generator for the associated construction + # node(s) + cn = self._construction_graph.get_fignode_cn( + fignode + ) + # check mapping options + print("***Detect Nozzle Droplet Gen***") + for cn_part in cn.mapping_options: + if ( + cn_part.primitive.mint + != "NOZZLE DROPLET GENERATOR" + ): + # remove if its not nozzle droplet generator + cn.mapping_options.remove(cn_part) + else: + raise Exception( + "No scheme for assign METER after initial" + " droplet generation" + ) + else: + # get the sorted fignode + sorted_fignode = self._fig.get_fignode( + sorted_fignode_id + ) + if isinstance(sorted_fignode, Interaction): + if sorted_fignode.type is InteractionType.METER: + # check if node is connected to our node of + # interest + if sorted_fignode_id in self._fig.predecessors( + fignode_id + ): + is_first_metering_node = False + + # Rule 2 – Any +-, distribute nodes before % should be in continuous flow + # (figure out components for this) + # TODO - Go through each of the FIG nodes, if the fig node has + + for fignode_id in self._fig.nodes: + fignode = self._fig.get_fignode(fignode_id) + + # check if explicitly mapped + if not ConstructionNode(fignode.ID).is_explictly_mapped: + if isinstance(fignode, Interaction): + if ( + fignode.type is InteractionType.MIX + or fignode.type is InteractionType.SIEVE + ): + if self.__check_continuous(fignode_id): + # this is NOT continuous + raise Exception("flow before METER is not continuous") + + # Rule 3 – Any Remetering (%) should require a droplet breakdown and + # regeneration (Ask Ali) + # Rule 4 – Distribute network post Metering stage should be mapped to different + # kinds of separator / selection/ storage networks + # Rule 5 – If plus is shown between node that has % in pred and non % in pred, t + # hen its pico injection + + for fignode_id in self._fig.nodes: + fignode = self._fig.get_fignode(fignode_id) + + # check if map + if not ConstructionNode(fignode.ID).is_explictly_mapped: + if isinstance(fignode, Interaction): + # if + + if ( + fignode.type is InteractionType.MIX + or fignode.type is InteractionType.SIEVE + ): + # compare predecessors, not successors + # create warning for more than 2 (mix can have more than 2 inputs) + + meter_in_pred = [] + + # check pred + + for prednode_id in self._fig.predecessors(fignode_id): + # check the first pred + + meter_in_pred.append( + self.__search_predecessors( + prednode_id, InteractionType.METER + ) + ) + + numTrue = meter_in_pred.count(True) + + if fignode.type is InteractionType.MIX: + if numTrue == 1: + # this is a pico injection + cn = self._construction_graph.get_fignode_cn(fignode) + print("***Detect Pico Injector***") + # check mapping options + while self.__exist_in_cn(cn, "PICOINJECTOR"): + for cn_part in cn.mapping_options: + print("-", cn_part.primitive.mint) + if cn_part.primitive.mint != "PICOINJECTOR": + # remove if its not [ico injection + cn.mapping_options.remove(cn_part) + print("after----") + + for cn_part in cn.mapping_options: + print("-", cn_part.primitive.mint) + + # Rule 6 + elif numTrue == 2: + # this is a droplet merging + cn = self._construction_graph.get_fignode_cn(fignode) + + print("***Detect Droplet Merger***") + while self.__exist_in_cn(cn, "DROPLET MERGER"): + for cn_part in cn.mapping_options: + print("-", cn_part.primitive.mint) + if cn_part.primitive.mint != "DROPLET MERGER": + cn.mapping_options.remove(cn_part) + print("after----") + + for cn_part in cn.mapping_options: + print("-", cn_part.primitive.mint) + + else: + print("***Mixer***") + cn = self._construction_graph.get_fignode_cn(fignode) + while self.__exist_in_cn(cn, "MIXER"): + for cn_part in cn.mapping_options: + print("-", cn_part.primitive.mint) + if cn_part.primitive.mint != "MIXER": + cn.mapping_options.remove(cn_part) + + print("after------") + for cn_part in cn.mapping_options: + print("-", cn_part.primitive.mint) + pass + + # Rule 6 – if plus is sown between two nodes that has % in pred, then its + # droplet merging + # Rule 7 – TBD Rule for droplet splitting + # Finally just reduce the total number of mapping options if greater than 1 + super().reduce_mapping_options() + + def prune_variants(self, variants: List[ConstructionGraph]) -> None: + """ + Prunes the variants by removing the ones that are not feasible. + + :param variants: The variants to prune. + :return: None + """ + super().prune_variants(variants) + + @staticmethod + def __exist_in_cn(cn, mint_name): + """helper function to check if the construction node contains undesired mints. + + Args: + cn (ConstructionNode): ConstructionNode to be checked + mint_name (string): mint name to check + + Returns: + Bool: if mint names other than the specified mint_name is found, returns true. Else false. + """ + for cn_part in cn.mapping_options: + if cn_part.primitive.mint != mint_name: + return True + + return False + + def __search_predecessors(self, fignode_id, search_type): + """recursive function searches for the specified InteractionType in the + predecessors of the specified fignode_id + + Args: + fignode_id (elements in self._fig.nodes): Starting node to find the + predecessors + search_type (InteractionType): Interaction type to find in the predecessors + + Returns: + Bool: If found, returns true. Else false + """ + fignode = self._fig.get_fignode(fignode_id) + + if self.__check_if_type(fignode, search_type): + return True + + else: + for prednode_id in self._fig.predecessors(fignode_id): + if self.__search_predecessors(prednode_id, search_type): + return True + + return False + + @staticmethod + def __check_if_type(fignode, search_type): + """helper function for __search_predecessors and __check_continuous. Check if + the specified search_type matches to the fignode.type + + Args: + fignode (FIGNode): fignode that contains InteractionType as type + search_type ([type]): desired type to check + + Returns: + Bool: if fignode.type matches to search_type, returns true. Else false + """ + if isinstance(fignode, Interaction): + if fignode.type is search_type: + return True + + return False + + # this function checks if the predecessors before METER has METER or not. If not, + # continuous. + def __check_continuous(self, fignode_id): + """recursive function to check if the flow before METER is continuous at MIX or SIEVE + + Args: + fignode_id (elements in self._fig.nodes): Starting node to find predecessors + + Returns: + Bool: if METER is found in the predecessors of METER, returns true + (not continuous), Else false + """ + fignode = self._fig.get_fignode(fignode_id) + + if self.__check_if_type(fignode, InteractionType.METER): + for prednode_id in self._fig.predecessors(fignode_id): + if self.__search_predecessors(prednode_id, InteractionType.METER): + return True + + else: + for other_pred_id in self._fig.predecessors(fignode_id): + if self.__check_continuous(other_pred_id): + return True + + return False + + def size_netlist(self, device: MINTDevice) -> None: + """ + Sizes the device based on either lookup tables, inverse design algorithms, etc. + """ + dafd_adapter = DAFDAdapter(device) + # Default size for PORT is 2000 um + for component in device.components: + constraints = self._construction_graph.get_component_cn( + component + ).constraints + if component.entity == "NOZZLE DROPLET GENERATOR": + # dafd_adapter.size_droplet_generator(component, constraints) + print("Skipping calling DAFD since its crashing everything right now") + elif component.entity == "PORT": + component.params.set_param("portRadius", 2000) diff --git a/old-stuff/dummy-old.py b/old-stuff/dummy-old.py new file mode 100644 index 0000000..6816d5b --- /dev/null +++ b/old-stuff/dummy-old.py @@ -0,0 +1,9 @@ +from lfr.fig.fluidinteractiongraph import FluidInteractionGraph +from lfr.netlistgenerator.gen_strategies.genstrategy import GenStrategy +from lfr.netlistgenerator.mappingoption import MappingOption + +# from lfr.netlistgenerator.constructiongraph import ConstructionGraph + + +class DummyStrategy(GenStrategy): + pass diff --git a/generator-old.py b/old-stuff/generator-old.py similarity index 98% rename from generator-old.py rename to old-stuff/generator-old.py index 6dfaa5a..18dc575 100644 --- a/generator-old.py +++ b/old-stuff/generator-old.py @@ -1,32 +1,33 @@ -from lfr.netlistgenerator.procedural_component_algorithms.ytree import YTREE -from lfr.netlistgenerator.gen_strategies.dropxstrategy import DropXStrategy -from lfr.fig.fluidinteractiongraph import FluidInteractionGraph -from lfr.postprocessor.mapping import NetworkMapping, NodeMappingTemplate -from pymint.mintlayer import MINTLayerType -from lfr.netlistgenerator.primitive import NetworkPrimitive, Primitive, PrimitiveType -from lfr.netlistgenerator.connectingoption import ConnectingOption -from lfr.netlistgenerator.mappinglibrary import MappingLibrary -from lfr.netlistgenerator.networkmappingoption import ( - NetworkMappingOption, - NetworkMappingOptionType, -) -from lfr.netlistgenerator.gen_strategies.genstrategy import GenStrategy -from lfr.fig.fignode import IOType, ValueNode from typing import List + +import networkx as nx from pymint.mintdevice import MINTDevice -from lfr.netlistgenerator.namegenerator import NameGenerator -from lfr.netlistgenerator.gen_strategies.dummy import DummyStrategy -from lfr.netlistgenerator.constructionnode import ConstructionNode -from lfr.netlistgenerator.constructiongraph import ConstructionGraph +from pymint.mintlayer import MINTLayerType + +from lfr.compiler.module import Module +from lfr.fig.fignode import IOType, ValueNode +from lfr.fig.fluidinteractiongraph import FluidInteractionGraph from lfr.fig.interaction import ( FluidIntegerInteraction, FluidNumberInteraction, InteractionType, ) +from lfr.netlistgenerator.connectingoption import ConnectingOption +from lfr.netlistgenerator.constructiongraph import ConstructionGraph +from lfr.netlistgenerator.constructionnode import ConstructionNode +from lfr.netlistgenerator.gen_strategies.dropxstrategy import DropXStrategy +from lfr.netlistgenerator.gen_strategies.dummy import DummyStrategy +from lfr.netlistgenerator.gen_strategies.genstrategy import GenStrategy +from lfr.netlistgenerator.mappinglibrary import MappingLibrary from lfr.netlistgenerator.mappingoption import MappingOption -from lfr.compiler.module import Module -import networkx as nx - +from lfr.netlistgenerator.namegenerator import NameGenerator +from lfr.netlistgenerator.networkmappingoption import ( + NetworkMappingOption, + NetworkMappingOptionType, +) +from lfr.netlistgenerator.primitive import NetworkPrimitive, Primitive, PrimitiveType +from lfr.netlistgenerator.procedural_component_algorithms.ytree import YTREE +from lfr.postprocessor.mapping import NetworkMapping, NodeMappingTemplate # def generate_MARS_library() -> MappingLibrary: # # TODO - Programatically create each of the items necessary for the MARS primitive library, @@ -42,7 +43,6 @@ def generate_dropx_library() -> MappingLibrary: - library = MappingLibrary("dropx") # PORT @@ -450,7 +450,6 @@ def generate_dropx_library() -> MappingLibrary: def generate(module: Module, library: MappingLibrary) -> MINTDevice: - construction_graph = ConstructionGraph() name_generator = NameGenerator() @@ -595,11 +594,10 @@ def override_mappings( assign_node_index = 0 for mapping in mappings: for instance in mapping.instances: - primitive_to_use = None if mapping.technology_string is not None: # Create a mapping option from the library with the corresponding info - primitive_to_use = mapping_library.get_primitive( + primitive_to_use = mapping_library.get_primitives( mapping.technology_string ) @@ -714,11 +712,10 @@ def override_network_mappings( assign_node_index = 0 for mapping in mappings: for instance in mapping.instances: - primitive_to_use = None if mapping.technology_string is not None: # Create a mapping option from the library with the corresponding info - primitive_to_use = mapping_library.get_primitive( + primitive_to_use = mapping_library.get_primitives( mapping.technology_string ) @@ -825,7 +822,6 @@ def eliminate_passthrough_nodes(construction_graph: ConstructionGraph): mapping_option = cn.mapping_options[0] if isinstance(mapping_option, NetworkMappingOption): if mapping_option.mapping_type is NetworkMappingOptionType.PASS_THROUGH: - print("Eliminating PASS THROUGH construction node = {}".format(cn.ID)) # First get all the in and out edges @@ -932,21 +928,3 @@ def size_netlist(): # Size all the Meter/Dilute/Divide nodes based on the value nodes # TODO - Talk to Ali about this for strategy construction_graph.size_components() - - -def __check_if_passthrough(sub) -> bool: - # Return true if its a single chain of flow channels - in_count = 0 - out_count = 0 - for node in list(sub.nodes): - inedges = list(sub.in_edges(node)) - outedges = list(sub.out_edges(node)) - if len(inedges) == 0: - in_count += 1 - if len(outedges) == 0: - out_count += 1 - - if in_count == 1 and out_count == 1: - return True - else: - return False diff --git a/old-stuff/genstrategy-old.py b/old-stuff/genstrategy-old.py new file mode 100644 index 0000000..95b4999 --- /dev/null +++ b/old-stuff/genstrategy-old.py @@ -0,0 +1,130 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING, Dict + +from pymint.mintlayer import MINTLayerType + +from lfr.fig.fluidinteractiongraph import FluidInteractionGraph + +if TYPE_CHECKING: + from lfr.netlistgenerator.constructiongraph import ConstructionGraph + +from typing import List + +from parchmint import Target +from pymint.mintdevice import MINTDevice +from pymint.mintnode import MINTNode + +from lfr.netlistgenerator.connectingoption import ConnectingOption + + +class GenStrategy: + def __init__(self, fig: FluidInteractionGraph) -> None: + self._fig: FluidInteractionGraph = fig + self._fig_netlist_map: Dict[str, str] = {} + + def reduce_mapping_options(self) -> None: + # Dummy strategy + # for cn in self._construction_graph.construction_nodes: + # # print(len(cn.mapping_options)) + # # clean this + # # Remove the extra mappings + # print( + # "Reducing mapping options for Construction node: {} from {} to {}" + # .format(cn.ID, len(cn.mapping_options), 1), + # ) + # if len(cn.mapping_options) > 1: + # for option in cn.mapping_options: + # print(" -{}".format(option.primitive.mint)) + # del cn.mapping_options[1 : len(cn.mapping_options)] + # # print("... -> {}".format(len(cn.mapping_options))) + + # print("Printing all final mapping options:") + # for cn in self._construction_graph.construction_nodes: + # print("Construction node: {}".format(cn.ID)) + # print("Options: ") + + # for mapping_option in cn.mapping_options: + # print(mapping_option.primitive.mint) + pass + + def generate_flow_network(self, fig_subgraph_view) -> MINTDevice: + # TODO - For now just assume that the networks basically are a bunch + # of nodes with nets/channels connecting them + ret = MINTDevice("flow_network_temp") + mint_layer = ret.create_mint_layer("0", "0", 0, MINTLayerType.FLOW) + for node in fig_subgraph_view.nodes: + n = MINTNode("node_{}".format(node), mint_layer) + ret.device.add_component(n.component) + self._store_fig_netlist_name(node, n.component.ID) + + i = 1 + for node in fig_subgraph_view.nodes: + # Create the channel between these nodes + channel_name = "c_{}".format(i) + i += 1 + params = {} + params["channelWidth"] = 400 + source = Target("node_{}".format(node)) + sinks = [] + + # Add all the outgoing edges + for edge in fig_subgraph_view.out_edges(node): + tar = edge[1] + if tar not in fig_subgraph_view.nodes: + # We skip because this might be a weird edge that we + # were not supposed to have in this network + continue + + sinks.append(Target("node_{}".format(tar))) + + ret.create_mint_connection( + channel_name, "CHANNEL", params, source, sinks, "0" + ) + + return ret + + def _store_fig_netlist_name(self, fig_id: str, netlist_id: str) -> None: + self._fig_netlist_map[fig_id] = netlist_id + + def _get_fig_netlist_name(self, fig_id: str) -> str: + return self._fig_netlist_map[fig_id] + + def generate_input_connectingoptions(self, subgraph_view) -> List[ConnectingOption]: + subgraph_inputs = [] + for node in list(subgraph_view.nodes): + if len(subgraph_view.in_edges(node)) == 0: + subgraph_inputs.append( + ConnectingOption(self._get_fig_netlist_name(node)) + ) + + return subgraph_inputs + + def generate_output_connectingoptions( + self, subgraph_view + ) -> List[ConnectingOption]: + subgraph_outputs = [] + for node in list(subgraph_view.nodes): + if len(subgraph_view.out_edges(node)) == 0: + subgraph_outputs.append( + ConnectingOption(self._get_fig_netlist_name(node)) + ) + + return subgraph_outputs + + @staticmethod + def generate_carrier_connectingoptions(subgraph_view) -> List[ConnectingOption]: + return [] + + @staticmethod + def generate_loading_connectingoptions(subgraph_view) -> List[ConnectingOption]: + return [] + + def size_netlist(self, device: MINTDevice) -> None: + raise NotImplementedError() + + def prune_variants(self, variants: List[ConstructionGraph]) -> None: + for variant in variants: + if variant.is_fig_fully_covered() is not True: + print("Removing variant (Construction Graph): {}".format(variant.ID)) + variants.remove(variant) diff --git a/old-stuff/mars-old.py b/old-stuff/mars-old.py new file mode 100644 index 0000000..510db5b --- /dev/null +++ b/old-stuff/mars-old.py @@ -0,0 +1,24 @@ +from typing import overload + +from lfr.fig.fluidinteractiongraph import FluidInteractionGraph +from lfr.netlistgenerator.gen_strategies.genstrategy import GenStrategy + + +class MARSStrategy(GenStrategy): + def __init__(self, fig: FluidInteractionGraph) -> None: + super().__init__(fig) + + def reduce_mapping_options(self) -> None: + # TODO - Implement Generalized Ali Strategy 1 + + # # Dummy strategy + # for cn in [v for k, v in self._construction_nodes.items()]: + # print(len(cn.mapping_options)) + # # Remove the extra mappings + # del cn.mapping_options[1 : len(cn.mapping_options)] + # print(len(cn.mapping_options)) + # pass + pass + + def size_netlist(self): + super() diff --git a/old-stuff/marsstrategy-old.py b/old-stuff/marsstrategy-old.py new file mode 100644 index 0000000..332701d --- /dev/null +++ b/old-stuff/marsstrategy-old.py @@ -0,0 +1,158 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING, Dict + +from pymint.mintlayer import MINTLayerType + +from lfr.fig.fluidinteractiongraph import FluidInteractionGraph +from lfr.netlistgenerator.constructiongraph.constructionnode import ConstructionNode +from lfr.netlistgenerator.dafdadapter import DAFDAdapter +from lfr.netlistgenerator.gen_strategies.genstrategy import GenStrategy + +if TYPE_CHECKING: + from lfr.netlistgenerator.constructiongraph.constructiongraph import ( + ConstructionGraph, + ) + +from typing import List + +from parchmint import Target +from pymint.mintdevice import MINTDevice +from pymint.mintnode import MINTNode + +from lfr.netlistgenerator.connectingoption import ConnectingOption + + +class MarsStrategy(GenStrategy): + def __init__(self, fig: FluidInteractionGraph) -> None: + self._fig: FluidInteractionGraph = fig + self._fig_netlist_map: Dict[str, str] = dict() + + def reduce_mapping_options(self) -> None: + # Dummy strategy + # for fignode_id in self._fig.nodes: + # fignode = self._fig.get_fignode(fignode_id) + + # if ConstructionNode(fignode_id).is_explictly_mapped: + # pass + # else: + # if isinstance(fignode, Interaction): + # cn = self._construction_graph.get_fignode_cn(fignode) + + # del cn.mapping_options[1 : len(cn.mapping_options)] + + # for cn in self._construction_graph.construction_nodes: + # # print(len(cn.mapping_options)) + # # clean this + # # Remove the extra mappings + # print( + # "Reducing mapping options for Construction node: {} from {} to {}".format( + # cn.id, len(cn.mapping_options), 1 + # ), + # ) + # if len(cn.mapping_options) > 1: + # for option in cn.mapping_options: + # print(" -{}".format(option.primitive.mint)) + # del cn.mapping_options[1 : len(cn.mapping_options)] + # # print("... -> {}".format(len(cn.mapping_options))) + + # print("Printing all final mapping options:") + # for cn in self._construction_graph.construction_nodes: + # print("Construction node: {}".format(cn.id)) + # print("Options: ") + + # for mapping_option in cn.mapping_options: + # print(mapping_option.primitive.mint) + pass + + def generate_flow_network(self, fig_subgraph_view) -> MINTDevice: + # TODO - For now just assume that the networks basically are a bunch + # of nodes with nets/channels connecting them + ret = MINTDevice("flow_network_temp") + mint_layer = ret.create_mint_layer("0", "0", 0, MINTLayerType.FLOW) + for node in fig_subgraph_view.nodes: + node = MINTNode("node_{}".format(str(node)), mint_layer) + ret.device.add_component(node.component) + # TODO - Add method to store NODES + self._store_fig_netlist_name(str(node), node.component.ID) + + i = 1 + for node in fig_subgraph_view.nodes: + # Create the channel between these nodes + channel_name = "c_{}".format(i) + i += 1 + params = dict() + params["channelWidth"] = 400 + source = Target("node_{}".format(node)) + sinks = [] + + # Add all the outgoing edges + for edge in fig_subgraph_view.out_edges(node): + tar = edge[1] + if tar not in fig_subgraph_view.nodes: + # We skip because this might be a weird edge that we were not supposed + # to have in this network + continue + + sinks.append(Target("node_{}".format(tar))) + + ret.create_mint_connection( + channel_name, "CHANNEL", params, source, sinks, "0" + ) + + return ret + + def _store_fig_netlist_name(self, fig_id: str, netlist_id: str) -> None: + self._fig_netlist_map[fig_id] = netlist_id + + def _get_fig_netlist_name(self, fig_id: str) -> str: + return self._fig_netlist_map[fig_id] + + def generate_input_connectingoptions(self, subgraph_view) -> List[ConnectingOption]: + subgraph_inputs = [] + for node in list(subgraph_view.nodes): + if len(subgraph_view.in_edges(node)) == 0: + subgraph_inputs.append( + ConnectingOption(self._get_fig_netlist_name(node)) + ) + + return subgraph_inputs + + def generate_output_connectingoptions( + self, subgraph_view + ) -> List[ConnectingOption]: + subgraph_outputs = [] + for node in list(subgraph_view.nodes): + if len(subgraph_view.out_edges(node)) == 0: + subgraph_outputs.append( + ConnectingOption(self._get_fig_netlist_name(node)) + ) + + return subgraph_outputs + + def generate_carrier_connectingoptions( + self, subgraph_view + ) -> List[ConnectingOption]: + return [] + + def generate_loading_connectingoptions( + self, subgraph_view + ) -> List[ConnectingOption]: + return [] + + def size_netlist(self, device: MINTDevice) -> None: + """ + Sizes the device based on either lookup tables, inverse design algorithms, etc. + """ + # dafd_adapter = DAFDAdapter(device) + # # Default size for PORT is 2000 um + # for component in device.components: + # constraints = self._construction_graph.get_component_cn( + # component + # ).constraints + # if component.entity == "NOZZLE DROPLET GENERATOR": + # # dafd_adapter.size_droplet_generator(component, constraints) + # print("Skipping calling DAFD since its crashing everything right now") + # elif component.entity == "PORT": + # component.params.set_param("portRadius", 2000) + pass diff --git a/poetry.lock b/poetry.lock index 81ddbc7..1c78ee7 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,18 +1,40 @@ +# This file is automatically @generated by Poetry 1.4.0 and should not be changed by hand. + [[package]] -name = "antlr4-python3-runtime" -version = "4.8" -description = "ANTLR 4.8 runtime for Python 3.7" +name = "absl-py" +version = "1.4.0" +description = "Abseil Python Common Libraries, see https://github.com/abseil/abseil-py." category = "main" optional = false -python-versions = "*" +python-versions = ">=3.6" +files = [ + {file = "absl-py-1.4.0.tar.gz", hash = "sha256:d2c244d01048ba476e7c080bd2c6df5e141d211de80223460d5b3b8a2a58433d"}, + {file = "absl_py-1.4.0-py3-none-any.whl", hash = "sha256:0d3fe606adfa4f7db64792dd4c7aee4ee0c38ab75dfd353b7a83ed3e957fcb47"}, +] [[package]] -name = "appdirs" -version = "1.4.4" -description = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." +name = "alabaster" +version = "0.7.13" +description = "A configurable sidebar-enabled Sphinx theme" category = "dev" optional = false +python-versions = ">=3.6" +files = [ + {file = "alabaster-0.7.13-py3-none-any.whl", hash = "sha256:1ee19aca801bbabb5ba3f5f258e4422dfa86f82f3e9cefb0859b283cdd7f62a3"}, + {file = "alabaster-0.7.13.tar.gz", hash = "sha256:a27a4a084d5e690e16e01e03ad2b2e552c61a65469419b907243193de1a84ae2"}, +] + +[[package]] +name = "antlr4-python3-runtime" +version = "4.12.0" +description = "ANTLR 4.12.0 runtime for Python 3" +category = "main" +optional = false python-versions = "*" +files = [ + {file = "antlr4-python3-runtime-4.12.0.tar.gz", hash = "sha256:0a8b82f55032734f43ed6b60b8a48c25754721a75cd714eb1fe9ce6ed418b361"}, + {file = "antlr4_python3_runtime-4.12.0-py3-none-any.whl", hash = "sha256:2c08f4dfbdc7dfd10f680681a96a55579c1e4f866f01d27099c9a54027923d70"}, +] [[package]] name = "argparse" @@ -21,61 +43,345 @@ description = "Python command-line parsing library" category = "main" optional = false python-versions = "*" +files = [ + {file = "argparse-1.4.0-py2.py3-none-any.whl", hash = "sha256:c31647edb69fd3d465a847ea3157d37bed1f95f19760b11a47aa91c04b666314"}, + {file = "argparse-1.4.0.tar.gz", hash = "sha256:62b089a55be1d8949cd2bc7e0df0bddb9e028faefc8c32038cc84862aefdd6e4"}, +] [[package]] name = "art" -version = "5.2" +version = "5.8" description = "ASCII Art Library For Python" category = "main" optional = false python-versions = ">=2.7" +files = [ + {file = "art-5.8-py2.py3-none-any.whl", hash = "sha256:cd77a9dcfe49dfb82f37a7b2401ae771fde6e155942b4e3dbc50a68c2bc7027c"}, + {file = "art-5.8.tar.gz", hash = "sha256:83e383910e07eb7844a6618498708e0080ea842d584128ee6b0dc62d8b48cf36"}, +] [package.extras] -dev = ["coverage (>=4.1)", "codecov (>=2.0.15)", "vulture (>=1.0)", "bandit (>=1.5.1)", "pydocstyle (>=3.0.0)"] +dev = ["bandit (>=1.5.1)", "codecov (>=2.0.15)", "coverage (>=4.1)", "pydocstyle (>=3.0.0)", "vulture (>=1.0)"] + +[[package]] +name = "astunparse" +version = "1.6.3" +description = "An AST unparser for Python" +category = "main" +optional = false +python-versions = "*" +files = [ + {file = "astunparse-1.6.3-py2.py3-none-any.whl", hash = "sha256:c2652417f2c8b5bb325c885ae329bdf3f86424075c4fd1a128674bc6fba4b8e8"}, + {file = "astunparse-1.6.3.tar.gz", hash = "sha256:5ad93a8456f0d084c3456d059fd9a92cce667963232cbf763eac3bc5b7940872"}, +] + +[package.dependencies] +six = ">=1.6.1,<2.0" +wheel = ">=0.23.0,<1.0" [[package]] name = "attrs" -version = "21.2.0" +version = "22.2.0" description = "Classes Without Boilerplate" category = "main" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +python-versions = ">=3.6" +files = [ + {file = "attrs-22.2.0-py3-none-any.whl", hash = "sha256:29e95c7f6778868dbd49170f98f8818f78f3dc5e0e37c0b1f474e3561b240836"}, + {file = "attrs-22.2.0.tar.gz", hash = "sha256:c9227bfc2f01993c03f68db37d1d15c9690188323c067c641f1a35ca58185f99"}, +] [package.extras] -dev = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface", "furo", "sphinx", "sphinx-notfound-page", "pre-commit"] -docs = ["furo", "sphinx", "zope.interface", "sphinx-notfound-page"] -tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface"] -tests_no_zope = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins"] +cov = ["attrs[tests]", "coverage-enable-subprocess", "coverage[toml] (>=5.3)"] +dev = ["attrs[docs,tests]"] +docs = ["furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier", "zope.interface"] +tests = ["attrs[tests-no-zope]", "zope.interface"] +tests-no-zope = ["cloudpickle", "cloudpickle", "hypothesis", "hypothesis", "mypy (>=0.971,<0.990)", "mypy (>=0.971,<0.990)", "pympler", "pympler", "pytest (>=4.3.0)", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-mypy-plugins", "pytest-xdist[psutil]", "pytest-xdist[psutil]"] + +[[package]] +name = "babel" +version = "2.12.1" +description = "Internationalization utilities" +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "Babel-2.12.1-py3-none-any.whl", hash = "sha256:b4246fb7677d3b98f501a39d43396d3cafdc8eadb045f4a31be01863f655c610"}, + {file = "Babel-2.12.1.tar.gz", hash = "sha256:cc2d99999cd01d44420ae725a21c9e3711b3aadc7976d6147f622d8581963455"}, +] + +[package.dependencies] +pytz = {version = ">=2015.7", markers = "python_version < \"3.9\""} [[package]] name = "black" -version = "20.8b1" +version = "22.3.0" description = "The uncompromising code formatter." category = "dev" optional = false -python-versions = ">=3.6" +python-versions = ">=3.6.2" +files = [ + {file = "black-22.3.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:2497f9c2386572e28921fa8bec7be3e51de6801f7459dffd6e62492531c47e09"}, + {file = "black-22.3.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5795a0375eb87bfe902e80e0c8cfaedf8af4d49694d69161e5bd3206c18618bb"}, + {file = "black-22.3.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e3556168e2e5c49629f7b0f377070240bd5511e45e25a4497bb0073d9dda776a"}, + {file = "black-22.3.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:67c8301ec94e3bcc8906740fe071391bce40a862b7be0b86fb5382beefecd968"}, + {file = "black-22.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:fd57160949179ec517d32ac2ac898b5f20d68ed1a9c977346efbac9c2f1e779d"}, + {file = "black-22.3.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:cc1e1de68c8e5444e8f94c3670bb48a2beef0e91dddfd4fcc29595ebd90bb9ce"}, + {file = "black-22.3.0-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6d2fc92002d44746d3e7db7cf9313cf4452f43e9ea77a2c939defce3b10b5c82"}, + {file = "black-22.3.0-cp36-cp36m-win_amd64.whl", hash = "sha256:a6342964b43a99dbc72f72812bf88cad8f0217ae9acb47c0d4f141a6416d2d7b"}, + {file = "black-22.3.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:328efc0cc70ccb23429d6be184a15ce613f676bdfc85e5fe8ea2a9354b4e9015"}, + {file = "black-22.3.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:06f9d8846f2340dfac80ceb20200ea5d1b3f181dd0556b47af4e8e0b24fa0a6b"}, + {file = "black-22.3.0-cp37-cp37m-win_amd64.whl", hash = "sha256:ad4efa5fad66b903b4a5f96d91461d90b9507a812b3c5de657d544215bb7877a"}, + {file = "black-22.3.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:e8477ec6bbfe0312c128e74644ac8a02ca06bcdb8982d4ee06f209be28cdf163"}, + {file = "black-22.3.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:637a4014c63fbf42a692d22b55d8ad6968a946b4a6ebc385c5505d9625b6a464"}, + {file = "black-22.3.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:863714200ada56cbc366dc9ae5291ceb936573155f8bf8e9de92aef51f3ad0f0"}, + {file = "black-22.3.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:10dbe6e6d2988049b4655b2b739f98785a884d4d6b85bc35133a8fb9a2233176"}, + {file = "black-22.3.0-cp38-cp38-win_amd64.whl", hash = "sha256:cee3e11161dde1b2a33a904b850b0899e0424cc331b7295f2a9698e79f9a69a0"}, + {file = "black-22.3.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:5891ef8abc06576985de8fa88e95ab70641de6c1fca97e2a15820a9b69e51b20"}, + {file = "black-22.3.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:30d78ba6bf080eeaf0b7b875d924b15cd46fec5fd044ddfbad38c8ea9171043a"}, + {file = "black-22.3.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ee8f1f7228cce7dffc2b464f07ce769f478968bfb3dd1254a4c2eeed84928aad"}, + {file = "black-22.3.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6ee227b696ca60dd1c507be80a6bc849a5a6ab57ac7352aad1ffec9e8b805f21"}, + {file = "black-22.3.0-cp39-cp39-win_amd64.whl", hash = "sha256:9b542ced1ec0ceeff5b37d69838106a6348e60db7b8fdd245294dc1d26136265"}, + {file = "black-22.3.0-py3-none-any.whl", hash = "sha256:bc58025940a896d7e5356952228b68f793cf5fcb342be703c3a2669a1488cb72"}, + {file = "black-22.3.0.tar.gz", hash = "sha256:35020b8886c022ced9282b51b5a875b6d1ab0c387b31a065b84db7c33085ca79"}, +] [package.dependencies] -appdirs = "*" -click = ">=7.1.2" +click = ">=8.0.0" mypy-extensions = ">=0.4.3" -pathspec = ">=0.6,<1" -regex = ">=2020.1.8" -toml = ">=0.10.1" -typed-ast = ">=1.4.0" -typing-extensions = ">=3.7.4" +pathspec = ">=0.9.0" +platformdirs = ">=2" +tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} +typing-extensions = {version = ">=3.10.0.0", markers = "python_version < \"3.10\""} [package.extras] colorama = ["colorama (>=0.4.3)"] -d = ["aiohttp (>=3.3.2)", "aiohttp-cors"] +d = ["aiohttp (>=3.7.4)"] +jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] +uvloop = ["uvloop (>=0.15.2)"] + +[[package]] +name = "cachetools" +version = "5.3.0" +description = "Extensible memoizing collections and decorators" +category = "main" +optional = false +python-versions = "~=3.7" +files = [ + {file = "cachetools-5.3.0-py3-none-any.whl", hash = "sha256:429e1a1e845c008ea6c85aa35d4b98b65d6a9763eeef3e37e92728a12d1de9d4"}, + {file = "cachetools-5.3.0.tar.gz", hash = "sha256:13dfddc7b8df938c21a940dfa6557ce6e94a2f1cdfa58eb90c805721d58f2c14"}, +] + +[[package]] +name = "certifi" +version = "2022.12.7" +description = "Python package for providing Mozilla's CA Bundle." +category = "main" +optional = false +python-versions = ">=3.6" +files = [ + {file = "certifi-2022.12.7-py3-none-any.whl", hash = "sha256:4ad3232f5e926d6718ec31cfc1fcadfde020920e278684144551c91769c7bc18"}, + {file = "certifi-2022.12.7.tar.gz", hash = "sha256:35824b4c3a97115964b408844d64aa14db1cc518f6562e8d7261699d1350a9e3"}, +] + +[[package]] +name = "charset-normalizer" +version = "3.1.0" +description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." +category = "main" +optional = false +python-versions = ">=3.7.0" +files = [ + {file = "charset-normalizer-3.1.0.tar.gz", hash = "sha256:34e0a2f9c370eb95597aae63bf85eb5e96826d81e3dcf88b8886012906f509b5"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:e0ac8959c929593fee38da1c2b64ee9778733cdf03c482c9ff1d508b6b593b2b"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d7fc3fca01da18fbabe4625d64bb612b533533ed10045a2ac3dd194bfa656b60"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:04eefcee095f58eaabe6dc3cc2262f3bcd776d2c67005880894f447b3f2cb9c1"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:20064ead0717cf9a73a6d1e779b23d149b53daf971169289ed2ed43a71e8d3b0"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1435ae15108b1cb6fffbcea2af3d468683b7afed0169ad718451f8db5d1aff6f"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c84132a54c750fda57729d1e2599bb598f5fa0344085dbde5003ba429a4798c0"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:75f2568b4189dda1c567339b48cba4ac7384accb9c2a7ed655cd86b04055c795"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:11d3bcb7be35e7b1bba2c23beedac81ee893ac9871d0ba79effc7fc01167db6c"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:891cf9b48776b5c61c700b55a598621fdb7b1e301a550365571e9624f270c203"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:5f008525e02908b20e04707a4f704cd286d94718f48bb33edddc7d7b584dddc1"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:b06f0d3bf045158d2fb8837c5785fe9ff9b8c93358be64461a1089f5da983137"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:49919f8400b5e49e961f320c735388ee686a62327e773fa5b3ce6721f7e785ce"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:22908891a380d50738e1f978667536f6c6b526a2064156203d418f4856d6e86a"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-win32.whl", hash = "sha256:12d1a39aa6b8c6f6248bb54550efcc1c38ce0d8096a146638fd4738e42284448"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:65ed923f84a6844de5fd29726b888e58c62820e0769b76565480e1fdc3d062f8"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9a3267620866c9d17b959a84dd0bd2d45719b817245e49371ead79ed4f710d19"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6734e606355834f13445b6adc38b53c0fd45f1a56a9ba06c2058f86893ae8017"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f8303414c7b03f794347ad062c0516cee0e15f7a612abd0ce1e25caf6ceb47df"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aaf53a6cebad0eae578f062c7d462155eada9c172bd8c4d250b8c1d8eb7f916a"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3dc5b6a8ecfdc5748a7e429782598e4f17ef378e3e272eeb1340ea57c9109f41"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e1b25e3ad6c909f398df8921780d6a3d120d8c09466720226fc621605b6f92b1"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0ca564606d2caafb0abe6d1b5311c2649e8071eb241b2d64e75a0d0065107e62"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b82fab78e0b1329e183a65260581de4375f619167478dddab510c6c6fb04d9b6"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:bd7163182133c0c7701b25e604cf1611c0d87712e56e88e7ee5d72deab3e76b5"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:11d117e6c63e8f495412d37e7dc2e2fff09c34b2d09dbe2bee3c6229577818be"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:cf6511efa4801b9b38dc5546d7547d5b5c6ef4b081c60b23e4d941d0eba9cbeb"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:abc1185d79f47c0a7aaf7e2412a0eb2c03b724581139193d2d82b3ad8cbb00ac"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:cb7b2ab0188829593b9de646545175547a70d9a6e2b63bf2cd87a0a391599324"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-win32.whl", hash = "sha256:c36bcbc0d5174a80d6cccf43a0ecaca44e81d25be4b7f90f0ed7bcfbb5a00909"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:cca4def576f47a09a943666b8f829606bcb17e2bc2d5911a46c8f8da45f56755"}, + {file = "charset_normalizer-3.1.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:0c95f12b74681e9ae127728f7e5409cbbef9cd914d5896ef238cc779b8152373"}, + {file = "charset_normalizer-3.1.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fca62a8301b605b954ad2e9c3666f9d97f63872aa4efcae5492baca2056b74ab"}, + {file = "charset_normalizer-3.1.0-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ac0aa6cd53ab9a31d397f8303f92c42f534693528fafbdb997c82bae6e477ad9"}, + {file = "charset_normalizer-3.1.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c3af8e0f07399d3176b179f2e2634c3ce9c1301379a6b8c9c9aeecd481da494f"}, + {file = "charset_normalizer-3.1.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3a5fc78f9e3f501a1614a98f7c54d3969f3ad9bba8ba3d9b438c3bc5d047dd28"}, + {file = "charset_normalizer-3.1.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:628c985afb2c7d27a4800bfb609e03985aaecb42f955049957814e0491d4006d"}, + {file = "charset_normalizer-3.1.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:74db0052d985cf37fa111828d0dd230776ac99c740e1a758ad99094be4f1803d"}, + {file = "charset_normalizer-3.1.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:1e8fcdd8f672a1c4fc8d0bd3a2b576b152d2a349782d1eb0f6b8e52e9954731d"}, + {file = "charset_normalizer-3.1.0-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:04afa6387e2b282cf78ff3dbce20f0cc071c12dc8f685bd40960cc68644cfea6"}, + {file = "charset_normalizer-3.1.0-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:dd5653e67b149503c68c4018bf07e42eeed6b4e956b24c00ccdf93ac79cdff84"}, + {file = "charset_normalizer-3.1.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:d2686f91611f9e17f4548dbf050e75b079bbc2a82be565832bc8ea9047b61c8c"}, + {file = "charset_normalizer-3.1.0-cp37-cp37m-win32.whl", hash = "sha256:4155b51ae05ed47199dc5b2a4e62abccb274cee6b01da5b895099b61b1982974"}, + {file = "charset_normalizer-3.1.0-cp37-cp37m-win_amd64.whl", hash = "sha256:322102cdf1ab682ecc7d9b1c5eed4ec59657a65e1c146a0da342b78f4112db23"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:e633940f28c1e913615fd624fcdd72fdba807bf53ea6925d6a588e84e1151531"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:3a06f32c9634a8705f4ca9946d667609f52cf130d5548881401f1eb2c39b1e2c"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7381c66e0561c5757ffe616af869b916c8b4e42b367ab29fedc98481d1e74e14"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3573d376454d956553c356df45bb824262c397c6e26ce43e8203c4c540ee0acb"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e89df2958e5159b811af9ff0f92614dabf4ff617c03a4c1c6ff53bf1c399e0e1"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:78cacd03e79d009d95635e7d6ff12c21eb89b894c354bd2b2ed0b4763373693b"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:de5695a6f1d8340b12a5d6d4484290ee74d61e467c39ff03b39e30df62cf83a0"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1c60b9c202d00052183c9be85e5eaf18a4ada0a47d188a83c8f5c5b23252f649"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:f645caaf0008bacf349875a974220f1f1da349c5dbe7c4ec93048cdc785a3326"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:ea9f9c6034ea2d93d9147818f17c2a0860d41b71c38b9ce4d55f21b6f9165a11"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:80d1543d58bd3d6c271b66abf454d437a438dff01c3e62fdbcd68f2a11310d4b"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:73dc03a6a7e30b7edc5b01b601e53e7fc924b04e1835e8e407c12c037e81adbd"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6f5c2e7bc8a4bf7c426599765b1bd33217ec84023033672c1e9a8b35eaeaaaf8"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-win32.whl", hash = "sha256:12a2b561af122e3d94cdb97fe6fb2bb2b82cef0cdca131646fdb940a1eda04f0"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:3160a0fd9754aab7d47f95a6b63ab355388d890163eb03b2d2b87ab0a30cfa59"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:38e812a197bf8e71a59fe55b757a84c1f946d0ac114acafaafaf21667a7e169e"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6baf0baf0d5d265fa7944feb9f7451cc316bfe30e8df1a61b1bb08577c554f31"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:8f25e17ab3039b05f762b0a55ae0b3632b2e073d9c8fc88e89aca31a6198e88f"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3747443b6a904001473370d7810aa19c3a180ccd52a7157aacc264a5ac79265e"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b116502087ce8a6b7a5f1814568ccbd0e9f6cfd99948aa59b0e241dc57cf739f"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d16fd5252f883eb074ca55cb622bc0bee49b979ae4e8639fff6ca3ff44f9f854"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:21fa558996782fc226b529fdd2ed7866c2c6ec91cee82735c98a197fae39f706"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6f6c7a8a57e9405cad7485f4c9d3172ae486cfef1344b5ddd8e5239582d7355e"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:ac3775e3311661d4adace3697a52ac0bab17edd166087d493b52d4f4f553f9f0"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:10c93628d7497c81686e8e5e557aafa78f230cd9e77dd0c40032ef90c18f2230"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:6f4f4668e1831850ebcc2fd0b1cd11721947b6dc7c00bf1c6bd3c929ae14f2c7"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:0be65ccf618c1e7ac9b849c315cc2e8a8751d9cfdaa43027d4f6624bd587ab7e"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:53d0a3fa5f8af98a1e261de6a3943ca631c526635eb5817a87a59d9a57ebf48f"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-win32.whl", hash = "sha256:a04f86f41a8916fe45ac5024ec477f41f886b3c435da2d4e3d2709b22ab02af1"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:830d2948a5ec37c386d3170c483063798d7879037492540f10a475e3fd6f244b"}, + {file = "charset_normalizer-3.1.0-py3-none-any.whl", hash = "sha256:3d9098b479e78c85080c98e1e35ff40b4a31d8953102bb0fd7d1b6f8a2111a3d"}, +] [[package]] name = "click" -version = "7.1.2" +version = "8.1.3" description = "Composable command line interface toolkit" -category = "dev" +category = "main" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +python-versions = ">=3.7" +files = [ + {file = "click-8.1.3-py3-none-any.whl", hash = "sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48"}, + {file = "click-8.1.3.tar.gz", hash = "sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "platform_system == \"Windows\""} + +[[package]] +name = "colorama" +version = "0.4.6" +description = "Cross-platform colored terminal text." +category = "main" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +files = [ + {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, + {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, +] + +[[package]] +name = "contourpy" +version = "1.0.7" +description = "Python library for calculating contours of 2D quadrilateral grids" +category = "main" +optional = false +python-versions = ">=3.8" +files = [ + {file = "contourpy-1.0.7-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:95c3acddf921944f241b6773b767f1cbce71d03307270e2d769fd584d5d1092d"}, + {file = "contourpy-1.0.7-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:fc1464c97579da9f3ab16763c32e5c5d5bb5fa1ec7ce509a4ca6108b61b84fab"}, + {file = "contourpy-1.0.7-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8acf74b5d383414401926c1598ed77825cd530ac7b463ebc2e4f46638f56cce6"}, + {file = "contourpy-1.0.7-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1c71fdd8f1c0f84ffd58fca37d00ca4ebaa9e502fb49825484da075ac0b0b803"}, + {file = "contourpy-1.0.7-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f99e9486bf1bb979d95d5cffed40689cb595abb2b841f2991fc894b3452290e8"}, + {file = "contourpy-1.0.7-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:87f4d8941a9564cda3f7fa6a6cd9b32ec575830780677932abdec7bcb61717b0"}, + {file = "contourpy-1.0.7-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:9e20e5a1908e18aaa60d9077a6d8753090e3f85ca25da6e25d30dc0a9e84c2c6"}, + {file = "contourpy-1.0.7-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:a877ada905f7d69b2a31796c4b66e31a8068b37aa9b78832d41c82fc3e056ddd"}, + {file = "contourpy-1.0.7-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:6381fa66866b0ea35e15d197fc06ac3840a9b2643a6475c8fff267db8b9f1e69"}, + {file = "contourpy-1.0.7-cp310-cp310-win32.whl", hash = "sha256:3c184ad2433635f216645fdf0493011a4667e8d46b34082f5a3de702b6ec42e3"}, + {file = "contourpy-1.0.7-cp310-cp310-win_amd64.whl", hash = "sha256:3caea6365b13119626ee996711ab63e0c9d7496f65641f4459c60a009a1f3e80"}, + {file = "contourpy-1.0.7-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:ed33433fc3820263a6368e532f19ddb4c5990855e4886088ad84fd7c4e561c71"}, + {file = "contourpy-1.0.7-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:38e2e577f0f092b8e6774459317c05a69935a1755ecfb621c0a98f0e3c09c9a5"}, + {file = "contourpy-1.0.7-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ae90d5a8590e5310c32a7630b4b8618cef7563cebf649011da80874d0aa8f414"}, + {file = "contourpy-1.0.7-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:130230b7e49825c98edf0b428b7aa1125503d91732735ef897786fe5452b1ec2"}, + {file = "contourpy-1.0.7-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:58569c491e7f7e874f11519ef46737cea1d6eda1b514e4eb5ac7dab6aa864d02"}, + {file = "contourpy-1.0.7-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:54d43960d809c4c12508a60b66cb936e7ed57d51fb5e30b513934a4a23874fae"}, + {file = "contourpy-1.0.7-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:152fd8f730c31fd67fe0ffebe1df38ab6a669403da93df218801a893645c6ccc"}, + {file = "contourpy-1.0.7-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:9056c5310eb1daa33fc234ef39ebfb8c8e2533f088bbf0bc7350f70a29bde1ac"}, + {file = "contourpy-1.0.7-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:a9d7587d2fdc820cc9177139b56795c39fb8560f540bba9ceea215f1f66e1566"}, + {file = "contourpy-1.0.7-cp311-cp311-win32.whl", hash = "sha256:4ee3ee247f795a69e53cd91d927146fb16c4e803c7ac86c84104940c7d2cabf0"}, + {file = "contourpy-1.0.7-cp311-cp311-win_amd64.whl", hash = "sha256:5caeacc68642e5f19d707471890f037a13007feba8427eb7f2a60811a1fc1350"}, + {file = "contourpy-1.0.7-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:fd7dc0e6812b799a34f6d12fcb1000539098c249c8da54f3566c6a6461d0dbad"}, + {file = "contourpy-1.0.7-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0f9d350b639db6c2c233d92c7f213d94d2e444d8e8fc5ca44c9706cf72193772"}, + {file = "contourpy-1.0.7-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:e96a08b62bb8de960d3a6afbc5ed8421bf1a2d9c85cc4ea73f4bc81b4910500f"}, + {file = "contourpy-1.0.7-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:031154ed61f7328ad7f97662e48660a150ef84ee1bc8876b6472af88bf5a9b98"}, + {file = "contourpy-1.0.7-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2e9ebb4425fc1b658e13bace354c48a933b842d53c458f02c86f371cecbedecc"}, + {file = "contourpy-1.0.7-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:efb8f6d08ca7998cf59eaf50c9d60717f29a1a0a09caa46460d33b2924839dbd"}, + {file = "contourpy-1.0.7-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:6c180d89a28787e4b73b07e9b0e2dac7741261dbdca95f2b489c4f8f887dd810"}, + {file = "contourpy-1.0.7-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:b8d587cc39057d0afd4166083d289bdeff221ac6d3ee5046aef2d480dc4b503c"}, + {file = "contourpy-1.0.7-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:769eef00437edf115e24d87f8926955f00f7704bede656ce605097584f9966dc"}, + {file = "contourpy-1.0.7-cp38-cp38-win32.whl", hash = "sha256:62398c80ef57589bdbe1eb8537127321c1abcfdf8c5f14f479dbbe27d0322e66"}, + {file = "contourpy-1.0.7-cp38-cp38-win_amd64.whl", hash = "sha256:57119b0116e3f408acbdccf9eb6ef19d7fe7baf0d1e9aaa5381489bc1aa56556"}, + {file = "contourpy-1.0.7-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:30676ca45084ee61e9c3da589042c24a57592e375d4b138bd84d8709893a1ba4"}, + {file = "contourpy-1.0.7-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3e927b3868bd1e12acee7cc8f3747d815b4ab3e445a28d2e5373a7f4a6e76ba1"}, + {file = "contourpy-1.0.7-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:366a0cf0fc079af5204801786ad7a1c007714ee3909e364dbac1729f5b0849e5"}, + {file = "contourpy-1.0.7-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:89ba9bb365446a22411f0673abf6ee1fea3b2cf47b37533b970904880ceb72f3"}, + {file = "contourpy-1.0.7-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:71b0bf0c30d432278793d2141362ac853859e87de0a7dee24a1cea35231f0d50"}, + {file = "contourpy-1.0.7-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e7281244c99fd7c6f27c1c6bfafba878517b0b62925a09b586d88ce750a016d2"}, + {file = "contourpy-1.0.7-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:b6d0f9e1d39dbfb3977f9dd79f156c86eb03e57a7face96f199e02b18e58d32a"}, + {file = "contourpy-1.0.7-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:7f6979d20ee5693a1057ab53e043adffa1e7418d734c1532e2d9e915b08d8ec2"}, + {file = "contourpy-1.0.7-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5dd34c1ae752515318224cba7fc62b53130c45ac6a1040c8b7c1a223c46e8967"}, + {file = "contourpy-1.0.7-cp39-cp39-win32.whl", hash = "sha256:c5210e5d5117e9aec8c47d9156d1d3835570dd909a899171b9535cb4a3f32693"}, + {file = "contourpy-1.0.7-cp39-cp39-win_amd64.whl", hash = "sha256:60835badb5ed5f4e194a6f21c09283dd6e007664a86101431bf870d9e86266c4"}, + {file = "contourpy-1.0.7-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:ce41676b3d0dd16dbcfabcc1dc46090aaf4688fd6e819ef343dbda5a57ef0161"}, + {file = "contourpy-1.0.7-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5a011cf354107b47c58ea932d13b04d93c6d1d69b8b6dce885e642531f847566"}, + {file = "contourpy-1.0.7-pp38-pypy38_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:31a55dccc8426e71817e3fe09b37d6d48ae40aae4ecbc8c7ad59d6893569c436"}, + {file = "contourpy-1.0.7-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:69f8ff4db108815addd900a74df665e135dbbd6547a8a69333a68e1f6e368ac2"}, + {file = "contourpy-1.0.7-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:efe99298ba37e37787f6a2ea868265465410822f7bea163edcc1bd3903354ea9"}, + {file = "contourpy-1.0.7-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:a1e97b86f73715e8670ef45292d7cc033548266f07d54e2183ecb3c87598888f"}, + {file = "contourpy-1.0.7-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cc331c13902d0f50845099434cd936d49d7a2ca76cb654b39691974cb1e4812d"}, + {file = "contourpy-1.0.7-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:24847601071f740837aefb730e01bd169fbcaa610209779a78db7ebb6e6a7051"}, + {file = "contourpy-1.0.7-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:abf298af1e7ad44eeb93501e40eb5a67abbf93b5d90e468d01fc0c4451971afa"}, + {file = "contourpy-1.0.7-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:64757f6460fc55d7e16ed4f1de193f362104285c667c112b50a804d482777edd"}, + {file = "contourpy-1.0.7.tar.gz", hash = "sha256:d8165a088d31798b59e91117d1f5fc3df8168d8b48c4acc10fc0df0d0bdbcc5e"}, +] + +[package.dependencies] +numpy = ">=1.16" + +[package.extras] +bokeh = ["bokeh", "chromedriver", "selenium"] +docs = ["furo", "sphinx-copybutton"] +mypy = ["contourpy[bokeh]", "docutils-stubs", "mypy (==0.991)", "types-Pillow"] +test = ["Pillow", "matplotlib", "pytest"] +test-no-images = ["pytest"] + +[[package]] +name = "cycler" +version = "0.11.0" +description = "Composable style cycles" +category = "main" +optional = false +python-versions = ">=3.6" +files = [ + {file = "cycler-0.11.0-py3-none-any.whl", hash = "sha256:3a27e95f763a428a739d2add979fa7494c912a32c17c4c38c4d5f082cad165a3"}, + {file = "cycler-0.11.0.tar.gz", hash = "sha256:9c87405839a19696e837b3b818fed3f5f69f16f1eec1a1ad77e043dcea9c772f"}, +] [[package]] name = "dafd" @@ -84,517 +390,2187 @@ description = "Python package for the library for DAFD." category = "main" optional = false python-versions = "^3.8" -develop = true +files = [] +develop = false [package.dependencies] -keras = ">=2.4.3,<3.0.0" -Markdown = ">=3.3.3,<4.0.0" -matplotlib = ">=3.3.1,<4.0.0" -pandas = ">=1.1.2,<2.0.0" -SALib = ">=1.3.12,<2.0.0" -scikit-learn = ">=0.23.2,<0.24.0" -seaborn = ">=0.11.1,<0.12.0" -tensorflow = ">=2.3.0,<3.0.0" -tqdm = ">=4.48.2,<5.0.0" +keras = "^2.4.3" +Markdown = "^3.3.3" +matplotlib = "^3.3.1" +pandas = "^1.1.2" +SALib = "^1.3.12" +scikit-learn = "^0.23.2" +seaborn = "^0.11.1" +tensorflow = "^2.3.0" +tqdm = "^4.48.2" [package.source] type = "directory" -url = "../dafd" +url = "dafd" [[package]] -name = "decorator" -version = "4.4.2" -description = "Decorators for Humans" +name = "dill" +version = "0.3.6" +description = "serialize all of python" category = "main" optional = false -python-versions = ">=2.6, !=3.0.*, !=3.1.*" +python-versions = ">=3.7" +files = [ + {file = "dill-0.3.6-py3-none-any.whl", hash = "sha256:a07ffd2351b8c678dfc4a856a3005f8067aea51d6ba6c700796a4d9e280f39f0"}, + {file = "dill-0.3.6.tar.gz", hash = "sha256:e5db55f3687856d8fbdab002ed78544e1c4559a130302693d839dfe8f93f2373"}, +] + +[package.extras] +graph = ["objgraph (>=1.7.2)"] + +[[package]] +name = "docutils" +version = "0.16" +description = "Docutils -- Python Documentation Utilities" +category = "dev" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +files = [ + {file = "docutils-0.16-py2.py3-none-any.whl", hash = "sha256:0c5b78adfbf7762415433f5515cd5c9e762339e23369dbe8000d84a4bf4ab3af"}, + {file = "docutils-0.16.tar.gz", hash = "sha256:c2de3a60e9e7d07be26b7f2b00ca0309c207e06c100f9cc2a94931fc75a478fc"}, +] + +[[package]] +name = "exceptiongroup" +version = "1.1.1" +description = "Backport of PEP 654 (exception groups)" +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "exceptiongroup-1.1.1-py3-none-any.whl", hash = "sha256:232c37c63e4f682982c8b6459f33a8981039e5fb8756b2074364e5055c498c9e"}, + {file = "exceptiongroup-1.1.1.tar.gz", hash = "sha256:d484c3090ba2889ae2928419117447a14daf3c1231d5e30d0aae34f354f01785"}, +] + +[package.extras] +test = ["pytest (>=6)"] [[package]] name = "flake8" -version = "3.8.4" +version = "3.9.2" description = "the modular source code checker: pep8 pyflakes and co" category = "dev" optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.7" +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" +files = [ + {file = "flake8-3.9.2-py2.py3-none-any.whl", hash = "sha256:bf8fd333346d844f616e8d47905ef3a3384edae6b4e9beb0c5101e25e3110907"}, + {file = "flake8-3.9.2.tar.gz", hash = "sha256:07528381786f2a6237b061f6e96610a4167b226cb926e2aa2b6b1d78057c576b"}, +] [package.dependencies] mccabe = ">=0.6.0,<0.7.0" -pycodestyle = ">=2.6.0a1,<2.7.0" -pyflakes = ">=2.2.0,<2.3.0" +pycodestyle = ">=2.7.0,<2.8.0" +pyflakes = ">=2.3.0,<2.4.0" [[package]] -name = "install" -version = "1.3.4" -description = "Install packages from within code" +name = "flatbuffers" +version = "23.3.3" +description = "The FlatBuffers serialization format for Python" category = "main" optional = false -python-versions = ">=2.7, >=3.5" +python-versions = "*" +files = [ + {file = "flatbuffers-23.3.3-py2.py3-none-any.whl", hash = "sha256:5ad36d376240090757e8f0a2cfaf6abcc81c6536c0dc988060375fd0899121f8"}, + {file = "flatbuffers-23.3.3.tar.gz", hash = "sha256:cabd87c4882f37840f6081f094b2c5bc28cefc2a6357732746936d055ab45c3d"}, +] [[package]] -name = "jsonschema" -version = "3.2.0" -description = "An implementation of JSON Schema validation for Python" +name = "fonttools" +version = "4.39.0" +description = "Tools to manipulate font files" category = "main" optional = false -python-versions = "*" - -[package.dependencies] -attrs = ">=17.4.0" -pyrsistent = ">=0.14.0" -six = ">=1.11.0" +python-versions = ">=3.8" +files = [ + {file = "fonttools-4.39.0-py3-none-any.whl", hash = "sha256:f5e764e1fd6ad54dfc201ff32af0ba111bcfbe0d05b24540af74c63db4ed6390"}, + {file = "fonttools-4.39.0.zip", hash = "sha256:909c104558835eac27faeb56be5a4c32694192dca123d073bf746ce9254054af"}, +] [package.extras] -format = ["idna", "jsonpointer (>1.13)", "rfc3987", "strict-rfc3339", "webcolors"] -format_nongpl = ["idna", "jsonpointer (>1.13)", "webcolors", "rfc3986-validator (>0.1.0)", "rfc3339-validator"] +all = ["brotli (>=1.0.1)", "brotlicffi (>=0.8.0)", "fs (>=2.2.0,<3)", "lxml (>=4.0,<5)", "lz4 (>=1.7.4.2)", "matplotlib", "munkres", "scipy", "skia-pathops (>=0.5.0)", "sympy", "uharfbuzz (>=0.23.0)", "unicodedata2 (>=15.0.0)", "xattr", "zopfli (>=0.1.4)"] +graphite = ["lz4 (>=1.7.4.2)"] +interpolatable = ["munkres", "scipy"] +lxml = ["lxml (>=4.0,<5)"] +pathops = ["skia-pathops (>=0.5.0)"] +plot = ["matplotlib"] +repacker = ["uharfbuzz (>=0.23.0)"] +symfont = ["sympy"] +type1 = ["xattr"] +ufo = ["fs (>=2.2.0,<3)"] +unicode = ["unicodedata2 (>=15.0.0)"] +woff = ["brotli (>=1.0.1)", "brotlicffi (>=0.8.0)", "zopfli (>=0.1.4)"] [[package]] -name = "mccabe" -version = "0.6.1" -description = "McCabe checker, plugin for flake8" -category = "dev" +name = "gast" +version = "0.4.0" +description = "Python AST that abstracts the underlying Python version" +category = "main" optional = false -python-versions = "*" +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +files = [ + {file = "gast-0.4.0-py3-none-any.whl", hash = "sha256:b7adcdd5adbebf1adf17378da5ba3f543684dbec47b1cda1f3997e573cd542c4"}, + {file = "gast-0.4.0.tar.gz", hash = "sha256:40feb7b8b8434785585ab224d1568b857edb18297e5a3047f1ba012bc83b42c1"}, +] [[package]] -name = "mypy" -version = "0.782" -description = "Optional static typing for Python" -category = "dev" +name = "google-auth" +version = "2.16.2" +description = "Google Authentication Library" +category = "main" optional = false -python-versions = ">=3.5" +python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*" +files = [ + {file = "google-auth-2.16.2.tar.gz", hash = "sha256:07e14f34ec288e3f33e00e2e3cc40c8942aa5d4ceac06256a28cd8e786591420"}, + {file = "google_auth-2.16.2-py2.py3-none-any.whl", hash = "sha256:2fef3cf94876d1a0e204afece58bb4d83fb57228aaa366c64045039fda6770a2"}, +] [package.dependencies] -mypy-extensions = ">=0.4.3,<0.5.0" -typed-ast = ">=1.4.0,<1.5.0" -typing-extensions = ">=3.7.4" +cachetools = ">=2.0.0,<6.0" +pyasn1-modules = ">=0.2.1" +rsa = {version = ">=3.1.4,<5", markers = "python_version >= \"3.6\""} +six = ">=1.9.0" [package.extras] -dmypy = ["psutil (>=4.0)"] - -[[package]] -name = "mypy-extensions" -version = "0.4.3" -description = "Experimental type system extensions for programs checked with the mypy typechecker." -category = "dev" -optional = false -python-versions = "*" +aiohttp = ["aiohttp (>=3.6.2,<4.0.0dev)", "requests (>=2.20.0,<3.0.0dev)"] +enterprise-cert = ["cryptography (==36.0.2)", "pyopenssl (==22.0.0)"] +pyopenssl = ["cryptography (>=38.0.3)", "pyopenssl (>=20.0.0)"] +reauth = ["pyu2f (>=0.1.5)"] +requests = ["requests (>=2.20.0,<3.0.0dev)"] [[package]] -name = "networkx" -version = "2.5" -description = "Python package for creating and manipulating graphs and networks" +name = "google-auth-oauthlib" +version = "0.4.6" +description = "Google Authentication Library" category = "main" optional = false python-versions = ">=3.6" +files = [ + {file = "google-auth-oauthlib-0.4.6.tar.gz", hash = "sha256:a90a072f6993f2c327067bf65270046384cda5a8ecb20b94ea9a687f1f233a7a"}, + {file = "google_auth_oauthlib-0.4.6-py2.py3-none-any.whl", hash = "sha256:3f2a6e802eebbb6fb736a370fbf3b055edcb6b52878bf2f26330b5e041316c73"}, +] [package.dependencies] -decorator = ">=4.3.0" +google-auth = ">=1.0.0" +requests-oauthlib = ">=0.7.0" [package.extras] -all = ["numpy", "scipy", "pandas", "matplotlib", "pygraphviz", "pydot", "pyyaml", "lxml", "pytest"] -gdal = ["gdal"] -lxml = ["lxml"] -matplotlib = ["matplotlib"] -numpy = ["numpy"] -pandas = ["pandas"] -pydot = ["pydot"] -pygraphviz = ["pygraphviz"] -pytest = ["pytest"] -pyyaml = ["pyyaml"] -scipy = ["scipy"] +tool = ["click (>=6.0.0)"] [[package]] -name = "numpy" -version = "1.19.5" -description = "NumPy is the fundamental package for array computing with Python." +name = "google-pasta" +version = "0.2.0" +description = "pasta is an AST-based Python refactoring library" category = "main" optional = false -python-versions = ">=3.6" +python-versions = "*" +files = [ + {file = "google-pasta-0.2.0.tar.gz", hash = "sha256:c9f2c8dfc8f96d0d5808299920721be30c9eec37f2389f28904f454565c8a16e"}, + {file = "google_pasta-0.2.0-py2-none-any.whl", hash = "sha256:4612951da876b1a10fe3960d7226f0c7682cf901e16ac06e473b267a5afa8954"}, + {file = "google_pasta-0.2.0-py3-none-any.whl", hash = "sha256:b32482794a366b5366a32c92a9a9201b107821889935a02b3e51f6b432ea84ed"}, +] + +[package.dependencies] +six = "*" [[package]] -name = "parchmint" -version = "0.2.9" -description = "Parchmint is data interchange standard for representing microfluidic designs. Check out https://parchmint.org for more information." +name = "grpcio" +version = "1.51.3" +description = "HTTP/2-based RPC framework" category = "main" optional = false -python-versions = "^3.8" -develop = true - -[package.dependencies] -argparse = ">=1.4.0,<2.0.0" -jsonschema = ">=3.2.0,<4.0.0" -networkx = ">=2.5,<3.0" +python-versions = ">=3.7" +files = [ + {file = "grpcio-1.51.3-cp310-cp310-linux_armv7l.whl", hash = "sha256:f601aaeae18dab81930fb8d4f916b0da21e89bb4b5f7367ef793f46b4a76b7b0"}, + {file = "grpcio-1.51.3-cp310-cp310-macosx_12_0_universal2.whl", hash = "sha256:eef0450a4b5ed11feab639bf3eb1b6e23d0efa9b911bf7b06fb60e14f5f8a585"}, + {file = "grpcio-1.51.3-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:82b0ad8ac825d4bb31bff9f638557c045f4a6d824d84b21e893968286f88246b"}, + {file = "grpcio-1.51.3-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3667c06e37d6cd461afdd51cefe6537702f3d1dc5ff4cac07e88d8b4795dc16f"}, + {file = "grpcio-1.51.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3709048fe0aa23dda09b3e69849a12055790171dab9e399a72ea8f9dfbf9ac80"}, + {file = "grpcio-1.51.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:200d69857f9910f7458b39b9bcf83ee4a180591b40146ba9e49314e3a7419313"}, + {file = "grpcio-1.51.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:cd9a5e68e79c5f031500e67793048a90209711e0854a9ddee8a3ce51728de4e5"}, + {file = "grpcio-1.51.3-cp310-cp310-win32.whl", hash = "sha256:6604f614016127ae10969176bbf12eb0e03d2fb3d643f050b3b69e160d144fb4"}, + {file = "grpcio-1.51.3-cp310-cp310-win_amd64.whl", hash = "sha256:e95c7ccd4c5807adef1602005513bf7c7d14e5a41daebcf9d8d30d8bf51b8f81"}, + {file = "grpcio-1.51.3-cp311-cp311-linux_armv7l.whl", hash = "sha256:5e77ee138100f0bb55cbd147840f87ee6241dbd25f09ea7cd8afe7efff323449"}, + {file = "grpcio-1.51.3-cp311-cp311-macosx_10_10_universal2.whl", hash = "sha256:68a7514b754e38e8de9075f7bb4dee919919515ec68628c43a894027e40ddec4"}, + {file = "grpcio-1.51.3-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3c1b9f8afa62ff265d86a4747a2990ec5a96e4efce5d5888f245a682d66eca47"}, + {file = "grpcio-1.51.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8de30f0b417744288cec65ec8cf84b8a57995cf7f1e84ccad2704d93f05d0aae"}, + {file = "grpcio-1.51.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:b69c7adc7ed60da1cb1b502853db61f453fc745f940cbcc25eb97c99965d8f41"}, + {file = "grpcio-1.51.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:d81528ffe0e973dc840ec73a4132fd18b8203ad129d7410155d951a0a7e4f5d0"}, + {file = "grpcio-1.51.3-cp311-cp311-win32.whl", hash = "sha256:040eb421613b57c696063abde405916dd830203c184c9000fc8c3b3b3c950325"}, + {file = "grpcio-1.51.3-cp311-cp311-win_amd64.whl", hash = "sha256:2a8e17286c4240137d933b8ca506465472248b4ce0fe46f3404459e708b65b68"}, + {file = "grpcio-1.51.3-cp37-cp37m-linux_armv7l.whl", hash = "sha256:d5cd1389669a847555df54177b911d9ff6f17345b2a6f19388707b7a9f724c88"}, + {file = "grpcio-1.51.3-cp37-cp37m-macosx_10_10_universal2.whl", hash = "sha256:be1bf35ce82cdbcac14e39d5102d8de4079a1c1a6a06b68e41fcd9ef64f9dd28"}, + {file = "grpcio-1.51.3-cp37-cp37m-manylinux_2_17_aarch64.whl", hash = "sha256:5eed34994c095e2bf7194ffac7381c6068b057ef1e69f8f08db77771350a7566"}, + {file = "grpcio-1.51.3-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3f9a7d88082b2a17ae7bd3c2354d13bab0453899e0851733f6afa6918373f476"}, + {file = "grpcio-1.51.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:36c8abbc5f837111e7bd619612eedc223c290b0903b952ce0c7b00840ea70f14"}, + {file = "grpcio-1.51.3-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:165b05af77e6aecb4210ae7663e25acf234ba78a7c1c157fa5f2efeb0d6ec53c"}, + {file = "grpcio-1.51.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:54e36c2ee304ff15f2bfbdc43d2b56c63331c52d818c364e5b5214e5bc2ad9f6"}, + {file = "grpcio-1.51.3-cp37-cp37m-win32.whl", hash = "sha256:cd0daac21d9ef5e033a5100c1d3aa055bbed28bfcf070b12d8058045c4e821b1"}, + {file = "grpcio-1.51.3-cp37-cp37m-win_amd64.whl", hash = "sha256:2fdd6333ce96435408565a9dbbd446212cd5d62e4d26f6a3c0feb1e3c35f1cc8"}, + {file = "grpcio-1.51.3-cp38-cp38-linux_armv7l.whl", hash = "sha256:54b0c29bdd9a3b1e1b61443ab152f060fc719f1c083127ab08d03fac5efd51be"}, + {file = "grpcio-1.51.3-cp38-cp38-macosx_10_10_universal2.whl", hash = "sha256:ffaaf7e93fcb437356b5a4b23bf36e8a3d0221399ff77fd057e4bc77776a24be"}, + {file = "grpcio-1.51.3-cp38-cp38-manylinux_2_17_aarch64.whl", hash = "sha256:eafbe7501a3268d05f2e450e1ddaffb950d842a8620c13ec328b501d25d2e2c3"}, + {file = "grpcio-1.51.3-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:881ecb34feabf31c6b3b9bbbddd1a5b57e69f805041e5a2c6c562a28574f71c4"}, + {file = "grpcio-1.51.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e860a3222139b41d430939bbec2ec9c3f6c740938bf7a04471a9a8caaa965a2e"}, + {file = "grpcio-1.51.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:49ede0528e9dac7e8a9fe30b16c73b630ddd9a576bf4b675eb6b0c53ee5ca00f"}, + {file = "grpcio-1.51.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6972b009638b40a448d10e1bc18e2223143b8a7aa20d7def0d78dd4af4126d12"}, + {file = "grpcio-1.51.3-cp38-cp38-win32.whl", hash = "sha256:5694448256e3cdfe5bd358f1574a3f2f51afa20cc834713c4b9788d60b7cc646"}, + {file = "grpcio-1.51.3-cp38-cp38-win_amd64.whl", hash = "sha256:3ea4341efe603b049e8c9a5f13c696ca37fcdf8a23ca35f650428ad3606381d9"}, + {file = "grpcio-1.51.3-cp39-cp39-linux_armv7l.whl", hash = "sha256:6c677581ce129f5fa228b8f418cee10bd28dd449f3a544ea73c8ba590ee49d0b"}, + {file = "grpcio-1.51.3-cp39-cp39-macosx_10_10_universal2.whl", hash = "sha256:30e09b5e0531685e176f49679b6a3b190762cc225f4565e55a899f5e14b3aa62"}, + {file = "grpcio-1.51.3-cp39-cp39-manylinux_2_17_aarch64.whl", hash = "sha256:c831f31336e81243f85b6daff3e5e8a123302ce0ea1f2726ad752fd7a59f3aee"}, + {file = "grpcio-1.51.3-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2cd2e4cefb724cab1ba2df4b7535a9980531b9ec51b4dbb5f137a1f3a3754ef0"}, + {file = "grpcio-1.51.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f7a0d0bf44438869d307f85a54f25a896ad6b4b0ca12370f76892ad732928d87"}, + {file = "grpcio-1.51.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:c02abd55409bfb293371554adf6a4401197ec2133dd97727c01180889014ba4d"}, + {file = "grpcio-1.51.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:2f8ff75e61e1227ba7a3f16b2eadbcc11d0a54096d52ab75a6b88cfbe56f55d1"}, + {file = "grpcio-1.51.3-cp39-cp39-win32.whl", hash = "sha256:6c99a73a6260bdf844b2e5ddad02dcd530310f80e1fa72c300fa19c1c7496962"}, + {file = "grpcio-1.51.3-cp39-cp39-win_amd64.whl", hash = "sha256:22bdfac4f7f27acdd4da359b5e7e1973dc74bf1ed406729b07d0759fde2f064b"}, + {file = "grpcio-1.51.3.tar.gz", hash = "sha256:be7b2265b7527bb12109a7727581e274170766d5b3c9258d4e466f4872522d7a"}, +] -[package.source] -type = "directory" -url = "../pyparchmint" +[package.extras] +protobuf = ["grpcio-tools (>=1.51.3)"] [[package]] -name = "pathspec" -version = "0.8.1" -description = "Utility library for gitignore style pattern matching of file paths." -category = "dev" +name = "h5py" +version = "3.8.0" +description = "Read and write HDF5 files from Python" +category = "main" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +python-versions = ">=3.7" +files = [ + {file = "h5py-3.8.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:533d7dad466ddb7e3b30af274b630eb7c1a6e4ddf01d1c373a0334dc2152110a"}, + {file = "h5py-3.8.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c873ba9fd4fa875ad62ce0e4891725e257a8fe7f5abdbc17e51a5d54819be55c"}, + {file = "h5py-3.8.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:98a240cd4c1bfd568aaa52ec42d263131a2582dab82d74d3d42a0d954cac12be"}, + {file = "h5py-3.8.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c3389b63222b1c7a158bb7fe69d11ca00066740ec5574596d47a2fe5317f563a"}, + {file = "h5py-3.8.0-cp310-cp310-win_amd64.whl", hash = "sha256:7f3350fc0a8407d668b13247861c2acd23f7f5fe7d060a3ad9b0820f5fcbcae0"}, + {file = "h5py-3.8.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:db03e3f2c716205fbdabb34d0848459840585225eb97b4f08998c743821ca323"}, + {file = "h5py-3.8.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:36761693efbe53df179627a775476dcbc37727d6e920958277a7efbc18f1fb73"}, + {file = "h5py-3.8.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4a506fc223def428f4329e7e1f9fe1c8c593eab226e7c0942c8d75308ad49950"}, + {file = "h5py-3.8.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:33b15aae79e9147aebe1d0e54099cbcde8d65e3e227cd5b59e49b1272aa0e09d"}, + {file = "h5py-3.8.0-cp311-cp311-win_amd64.whl", hash = "sha256:9f6f6ffadd6bfa9b2c5b334805eb4b19ca0a5620433659d8f7fb86692c40a359"}, + {file = "h5py-3.8.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:8f55d9c6c84d7d09c79fb85979e97b81ec6071cc776a97eb6b96f8f6ec767323"}, + {file = "h5py-3.8.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b685453e538b2b5934c58a644ac3f3b3d0cec1a01b6fb26d57388e9f9b674ad0"}, + {file = "h5py-3.8.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:377865821fe80ad984d003723d6f8890bd54ceeb5981b43c0313b9df95411b30"}, + {file = "h5py-3.8.0-cp37-cp37m-win_amd64.whl", hash = "sha256:0fef76e10b9216657fa37e7edff6d8be0709b25bd5066474c229b56cf0098df9"}, + {file = "h5py-3.8.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:26ffc344ec9984d2cd3ca0265007299a8bac8d85c1ad48f4639d8d3aed2af171"}, + {file = "h5py-3.8.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:bacaa1c16810dd2b3e4417f8e730971b7c4d53d234de61fe4a918db78e80e1e4"}, + {file = "h5py-3.8.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bae730580ae928de409d63cbe4fdca4c82c3ad2bed30511d19d34e995d63c77e"}, + {file = "h5py-3.8.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f47f757d1b76f0ecb8aa0508ec8d1b390df67a8b67ee2515dc1b046f3a1596ea"}, + {file = "h5py-3.8.0-cp38-cp38-win_amd64.whl", hash = "sha256:f891b17e3a3e974e93f9e34e7cca9f530806543571ce078998676a555837d91d"}, + {file = "h5py-3.8.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:290e00fa2de74a10688d1bac98d5a9cdd43f14f58e562c580b5b3dfbd358ecae"}, + {file = "h5py-3.8.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:03890b1c123d024fb0239a3279737d5432498c1901c354f8b10d8221d1d16235"}, + {file = "h5py-3.8.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b7865de06779b14d98068da387333ad9bf2756b5b579cc887fac169bc08f87c3"}, + {file = "h5py-3.8.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:49bc857635f935fa30e92e61ac1e87496df8f260a6945a3235e43a9890426866"}, + {file = "h5py-3.8.0-cp39-cp39-win_amd64.whl", hash = "sha256:5fd2252d1fc364ba0e93dd0b7089f4906b66805cb4e6aca7fa8874ac08649647"}, + {file = "h5py-3.8.0.tar.gz", hash = "sha256:6fead82f0c4000cf38d53f9c030780d81bfa0220218aee13b90b7701c937d95f"}, +] -[[package]] -name = "pycodestyle" -version = "2.6.0" -description = "Python style guide checker" -category = "dev" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +[package.dependencies] +numpy = ">=1.14.5" [[package]] -name = "pyeda" -version = "0.28.0" -description = "Python Electronic Design Automation" +name = "idna" +version = "3.4" +description = "Internationalized Domain Names in Applications (IDNA)" category = "main" optional = false -python-versions = "*" +python-versions = ">=3.5" +files = [ + {file = "idna-3.4-py3-none-any.whl", hash = "sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2"}, + {file = "idna-3.4.tar.gz", hash = "sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4"}, +] [[package]] -name = "pyflakes" -version = "2.2.0" -description = "passive checker of Python programs" +name = "imagesize" +version = "1.4.1" +description = "Getting image size from png/jpeg/jpeg2000/gif file" category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +files = [ + {file = "imagesize-1.4.1-py2.py3-none-any.whl", hash = "sha256:0d8d18d08f840c19d0ee7ca1fd82490fdc3729b7ac93f49870406ddde8ef8d8b"}, + {file = "imagesize-1.4.1.tar.gz", hash = "sha256:69150444affb9cb0d5cc5a92b3676f0b2fb7cd9ae39e947a5e11a36b4497cd4a"}, +] [[package]] -name = "pygraphviz" -version = "1.6" -description = "Python interface to Graphviz" +name = "importlib-metadata" +version = "6.0.0" +description = "Read metadata from Python packages" category = "main" optional = false -python-versions = ">=3.6" +python-versions = ">=3.7" +files = [ + {file = "importlib_metadata-6.0.0-py3-none-any.whl", hash = "sha256:7efb448ec9a5e313a57655d35aa54cd3e01b7e1fbcf72dce1bf06119420f5bad"}, + {file = "importlib_metadata-6.0.0.tar.gz", hash = "sha256:e354bedeb60efa6affdcc8ae121b73544a7aa74156d047311948f6d711cd378d"}, +] + +[package.dependencies] +zipp = ">=0.5" + +[package.extras] +docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +perf = ["ipython"] +testing = ["flake8 (<5)", "flufl.flake8", "importlib-resources (>=1.3)", "packaging", "pyfakefs", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)", "pytest-perf (>=0.9.2)"] [[package]] -name = "pymint" -version = "0.2.11" -description = "MINT is a human readable language to describe Microfluidic Hardware Netlists. MINT is the name of the Microfluidic Netlist language used to describe microfluidic devices for Fluigi to place and route. Mint is a flavor of (MHDL) Microfluidic Hardware Description Language that can be used to represent Microfluidic Circuits." +name = "importlib-resources" +version = "5.12.0" +description = "Read resources from Python packages" category = "main" optional = false -python-versions = ">=3.8,<4.0" +python-versions = ">=3.7" +files = [ + {file = "importlib_resources-5.12.0-py3-none-any.whl", hash = "sha256:7b1deeebbf351c7578e09bf2f63fa2ce8b5ffec296e0d349139d43cca061a81a"}, + {file = "importlib_resources-5.12.0.tar.gz", hash = "sha256:4be82589bf5c1d7999aedf2a45159d10cb3ca4f19b2271f8792bc8e6da7b22f6"}, +] [package.dependencies] -antlr4-python3-runtime = ">=4.8,<5.0" -install = ">=1.3.3,<2.0.0" -parchmint = ">=0.2.6,<0.3.0" +zipp = {version = ">=3.1.0", markers = "python_version < \"3.10\""} + +[package.extras] +docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +testing = ["flake8 (<5)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)"] [[package]] -name = "pyrsistent" -version = "0.18.0" -description = "Persistent/Functional/Immutable data structures" +name = "iniconfig" +version = "2.0.0" +description = "brain-dead simple config-ini parsing" +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"}, + {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, +] + +[[package]] +name = "install" +version = "1.3.5" +description = "Install packages from within code" category = "main" optional = false -python-versions = ">=3.6" +python-versions = ">=2.7, >=3.5" +files = [ + {file = "install-1.3.5-py3-none-any.whl", hash = "sha256:0d3fadf4aa62c95efe8d34757c8507eb46177f86c016c21c6551eafc6a53d5a9"}, + {file = "install-1.3.5.tar.gz", hash = "sha256:e67c8a0be5ccf8cb4ffa17d090f3a61b6e820e6a7e21cd1d2c0f7bc59b18e647"}, +] [[package]] -name = "regex" -version = "2020.11.11" -description = "Alternative regular expression module, to replace re." +name = "isort" +version = "5.12.0" +description = "A Python utility / library to sort Python imports." category = "dev" optional = false -python-versions = "*" +python-versions = ">=3.8.0" +files = [ + {file = "isort-5.12.0-py3-none-any.whl", hash = "sha256:f84c2818376e66cf843d497486ea8fed8700b340f308f076c6fb1229dff318b6"}, + {file = "isort-5.12.0.tar.gz", hash = "sha256:8bef7dde241278824a6d83f44a544709b065191b95b6e50894bdc722fcba0504"}, +] + +[package.extras] +colors = ["colorama (>=0.4.3)"] +pipfile-deprecated-finder = ["pip-shims (>=0.5.2)", "pipreqs", "requirementslib"] +plugins = ["setuptools"] +requirements-deprecated-finder = ["pip-api", "pipreqs"] [[package]] -name = "rope" -version = "0.18.0" -description = "a python refactoring library..." +name = "jinja2" +version = "3.1.2" +description = "A very fast and expressive template engine." category = "dev" optional = false -python-versions = "*" +python-versions = ">=3.7" +files = [ + {file = "Jinja2-3.1.2-py3-none-any.whl", hash = "sha256:6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61"}, + {file = "Jinja2-3.1.2.tar.gz", hash = "sha256:31351a702a408a9e7595a8fc6150fc3f43bb6bf7e319770cbc0db9df9437e852"}, +] + +[package.dependencies] +MarkupSafe = ">=2.0" [package.extras] -dev = ["pytest"] +i18n = ["Babel (>=2.7)"] [[package]] -name = "six" -version = "1.16.0" -description = "Python 2 and 3 compatibility utilities" +name = "joblib" +version = "1.2.0" +description = "Lightweight pipelining with Python functions" category = "main" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" +python-versions = ">=3.7" +files = [ + {file = "joblib-1.2.0-py3-none-any.whl", hash = "sha256:091138ed78f800342968c523bdde947e7a305b8594b910a0fea2ab83c3c6d385"}, + {file = "joblib-1.2.0.tar.gz", hash = "sha256:e1cee4a79e4af22881164f218d4311f60074197fb707e082e803b61f6d137018"}, +] [[package]] -name = "tabulate" -version = "0.8.9" -description = "Pretty-print tabular data" +name = "jsonschema" +version = "3.2.0" +description = "An implementation of JSON Schema validation for Python" category = "main" optional = false python-versions = "*" +files = [ + {file = "jsonschema-3.2.0-py2.py3-none-any.whl", hash = "sha256:4e5b3cf8216f577bee9ce139cbe72eca3ea4f292ec60928ff24758ce626cd163"}, + {file = "jsonschema-3.2.0.tar.gz", hash = "sha256:c8a85b28d377cc7737e46e2d9f2b4f44ee3c0e1deac6bf46ddefc7187d30797a"}, +] + +[package.dependencies] +attrs = ">=17.4.0" +pyrsistent = ">=0.14.0" +setuptools = "*" +six = ">=1.11.0" [package.extras] -widechars = ["wcwidth"] +format = ["idna", "jsonpointer (>1.13)", "rfc3987", "strict-rfc3339", "webcolors"] +format-nongpl = ["idna", "jsonpointer (>1.13)", "rfc3339-validator", "rfc3986-validator (>0.1.0)", "webcolors"] [[package]] -name = "toml" -version = "0.10.2" -description = "Python Library for Tom's Obvious, Minimal Language" -category = "dev" +name = "keras" +version = "2.11.0" +description = "Deep learning for humans." +category = "main" optional = false -python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" +python-versions = ">=3.7" +files = [ + {file = "keras-2.11.0-py2.py3-none-any.whl", hash = "sha256:38c6fff0ea9a8b06a2717736565c92a73c8cd9b1c239e7125ccb188b7848f65e"}, +] [[package]] -name = "typed-ast" -version = "1.4.1" -description = "a fork of Python 2 and 3 ast modules with type comment support" -category = "dev" +name = "kiwisolver" +version = "1.4.4" +description = "A fast implementation of the Cassowary constraint solver" +category = "main" optional = false -python-versions = "*" +python-versions = ">=3.7" +files = [ + {file = "kiwisolver-1.4.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:2f5e60fabb7343a836360c4f0919b8cd0d6dbf08ad2ca6b9cf90bf0c76a3c4f6"}, + {file = "kiwisolver-1.4.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:10ee06759482c78bdb864f4109886dff7b8a56529bc1609d4f1112b93fe6423c"}, + {file = "kiwisolver-1.4.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c79ebe8f3676a4c6630fd3f777f3cfecf9289666c84e775a67d1d358578dc2e3"}, + {file = "kiwisolver-1.4.4-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:abbe9fa13da955feb8202e215c4018f4bb57469b1b78c7a4c5c7b93001699938"}, + {file = "kiwisolver-1.4.4-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:7577c1987baa3adc4b3c62c33bd1118c3ef5c8ddef36f0f2c950ae0b199e100d"}, + {file = "kiwisolver-1.4.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f8ad8285b01b0d4695102546b342b493b3ccc6781fc28c8c6a1bb63e95d22f09"}, + {file = "kiwisolver-1.4.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8ed58b8acf29798b036d347791141767ccf65eee7f26bde03a71c944449e53de"}, + {file = "kiwisolver-1.4.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a68b62a02953b9841730db7797422f983935aeefceb1679f0fc85cbfbd311c32"}, + {file = "kiwisolver-1.4.4-cp310-cp310-win32.whl", hash = "sha256:e92a513161077b53447160b9bd8f522edfbed4bd9759e4c18ab05d7ef7e49408"}, + {file = "kiwisolver-1.4.4-cp310-cp310-win_amd64.whl", hash = "sha256:3fe20f63c9ecee44560d0e7f116b3a747a5d7203376abeea292ab3152334d004"}, + {file = "kiwisolver-1.4.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:e0ea21f66820452a3f5d1655f8704a60d66ba1191359b96541eaf457710a5fc6"}, + {file = "kiwisolver-1.4.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:bc9db8a3efb3e403e4ecc6cd9489ea2bac94244f80c78e27c31dcc00d2790ac2"}, + {file = "kiwisolver-1.4.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d5b61785a9ce44e5a4b880272baa7cf6c8f48a5180c3e81c59553ba0cb0821ca"}, + {file = "kiwisolver-1.4.4-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c2dbb44c3f7e6c4d3487b31037b1bdbf424d97687c1747ce4ff2895795c9bf69"}, + {file = "kiwisolver-1.4.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6295ecd49304dcf3bfbfa45d9a081c96509e95f4b9d0eb7ee4ec0530c4a96514"}, + {file = "kiwisolver-1.4.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4bd472dbe5e136f96a4b18f295d159d7f26fd399136f5b17b08c4e5f498cd494"}, + {file = "kiwisolver-1.4.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bf7d9fce9bcc4752ca4a1b80aabd38f6d19009ea5cbda0e0856983cf6d0023f5"}, + {file = "kiwisolver-1.4.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:78d6601aed50c74e0ef02f4204da1816147a6d3fbdc8b3872d263338a9052c51"}, + {file = "kiwisolver-1.4.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:877272cf6b4b7e94c9614f9b10140e198d2186363728ed0f701c6eee1baec1da"}, + {file = "kiwisolver-1.4.4-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:db608a6757adabb32f1cfe6066e39b3706d8c3aa69bbc353a5b61edad36a5cb4"}, + {file = "kiwisolver-1.4.4-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:5853eb494c71e267912275e5586fe281444eb5e722de4e131cddf9d442615626"}, + {file = "kiwisolver-1.4.4-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:f0a1dbdb5ecbef0d34eb77e56fcb3e95bbd7e50835d9782a45df81cc46949750"}, + {file = "kiwisolver-1.4.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:283dffbf061a4ec60391d51e6155e372a1f7a4f5b15d59c8505339454f8989e4"}, + {file = "kiwisolver-1.4.4-cp311-cp311-win32.whl", hash = "sha256:d06adcfa62a4431d404c31216f0f8ac97397d799cd53800e9d3efc2fbb3cf14e"}, + {file = "kiwisolver-1.4.4-cp311-cp311-win_amd64.whl", hash = "sha256:e7da3fec7408813a7cebc9e4ec55afed2d0fd65c4754bc376bf03498d4e92686"}, + {file = "kiwisolver-1.4.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:62ac9cc684da4cf1778d07a89bf5f81b35834cb96ca523d3a7fb32509380cbf6"}, + {file = "kiwisolver-1.4.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:41dae968a94b1ef1897cb322b39360a0812661dba7c682aa45098eb8e193dbdf"}, + {file = "kiwisolver-1.4.4-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:02f79693ec433cb4b5f51694e8477ae83b3205768a6fb48ffba60549080e295b"}, + {file = "kiwisolver-1.4.4-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d0611a0a2a518464c05ddd5a3a1a0e856ccc10e67079bb17f265ad19ab3c7597"}, + {file = "kiwisolver-1.4.4-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:db5283d90da4174865d520e7366801a93777201e91e79bacbac6e6927cbceede"}, + {file = "kiwisolver-1.4.4-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:1041feb4cda8708ce73bb4dcb9ce1ccf49d553bf87c3954bdfa46f0c3f77252c"}, + {file = "kiwisolver-1.4.4-cp37-cp37m-win32.whl", hash = "sha256:a553dadda40fef6bfa1456dc4be49b113aa92c2a9a9e8711e955618cd69622e3"}, + {file = "kiwisolver-1.4.4-cp37-cp37m-win_amd64.whl", hash = "sha256:03baab2d6b4a54ddbb43bba1a3a2d1627e82d205c5cf8f4c924dc49284b87166"}, + {file = "kiwisolver-1.4.4-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:841293b17ad704d70c578f1f0013c890e219952169ce8a24ebc063eecf775454"}, + {file = "kiwisolver-1.4.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:f4f270de01dd3e129a72efad823da90cc4d6aafb64c410c9033aba70db9f1ff0"}, + {file = "kiwisolver-1.4.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:f9f39e2f049db33a908319cf46624a569b36983c7c78318e9726a4cb8923b26c"}, + {file = "kiwisolver-1.4.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c97528e64cb9ebeff9701e7938653a9951922f2a38bd847787d4a8e498cc83ae"}, + {file = "kiwisolver-1.4.4-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1d1573129aa0fd901076e2bfb4275a35f5b7aa60fbfb984499d661ec950320b0"}, + {file = "kiwisolver-1.4.4-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ad881edc7ccb9d65b0224f4e4d05a1e85cf62d73aab798943df6d48ab0cd79a1"}, + {file = "kiwisolver-1.4.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b428ef021242344340460fa4c9185d0b1f66fbdbfecc6c63eff4b7c29fad429d"}, + {file = "kiwisolver-1.4.4-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:2e407cb4bd5a13984a6c2c0fe1845e4e41e96f183e5e5cd4d77a857d9693494c"}, + {file = "kiwisolver-1.4.4-cp38-cp38-win32.whl", hash = "sha256:75facbe9606748f43428fc91a43edb46c7ff68889b91fa31f53b58894503a191"}, + {file = "kiwisolver-1.4.4-cp38-cp38-win_amd64.whl", hash = "sha256:5bce61af018b0cb2055e0e72e7d65290d822d3feee430b7b8203d8a855e78766"}, + {file = "kiwisolver-1.4.4-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:8c808594c88a025d4e322d5bb549282c93c8e1ba71b790f539567932722d7bd8"}, + {file = "kiwisolver-1.4.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f0a71d85ecdd570ded8ac3d1c0f480842f49a40beb423bb8014539a9f32a5897"}, + {file = "kiwisolver-1.4.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b533558eae785e33e8c148a8d9921692a9fe5aa516efbdff8606e7d87b9d5824"}, + {file = "kiwisolver-1.4.4-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:efda5fc8cc1c61e4f639b8067d118e742b812c930f708e6667a5ce0d13499e29"}, + {file = "kiwisolver-1.4.4-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:7c43e1e1206cd421cd92e6b3280d4385d41d7166b3ed577ac20444b6995a445f"}, + {file = "kiwisolver-1.4.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bc8d3bd6c72b2dd9decf16ce70e20abcb3274ba01b4e1c96031e0c4067d1e7cd"}, + {file = "kiwisolver-1.4.4-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4ea39b0ccc4f5d803e3337dd46bcce60b702be4d86fd0b3d7531ef10fd99a1ac"}, + {file = "kiwisolver-1.4.4-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:968f44fdbf6dd757d12920d63b566eeb4d5b395fd2d00d29d7ef00a00582aac9"}, + {file = "kiwisolver-1.4.4-cp39-cp39-win32.whl", hash = "sha256:da7e547706e69e45d95e116e6939488d62174e033b763ab1496b4c29b76fabea"}, + {file = "kiwisolver-1.4.4-cp39-cp39-win_amd64.whl", hash = "sha256:ba59c92039ec0a66103b1d5fe588fa546373587a7d68f5c96f743c3396afc04b"}, + {file = "kiwisolver-1.4.4-pp37-pypy37_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:91672bacaa030f92fc2f43b620d7b337fd9a5af28b0d6ed3f77afc43c4a64b5a"}, + {file = "kiwisolver-1.4.4-pp37-pypy37_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:787518a6789009c159453da4d6b683f468ef7a65bbde796bcea803ccf191058d"}, + {file = "kiwisolver-1.4.4-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da152d8cdcab0e56e4f45eb08b9aea6455845ec83172092f09b0e077ece2cf7a"}, + {file = "kiwisolver-1.4.4-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:ecb1fa0db7bf4cff9dac752abb19505a233c7f16684c5826d1f11ebd9472b871"}, + {file = "kiwisolver-1.4.4-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:28bc5b299f48150b5f822ce68624e445040595a4ac3d59251703779836eceff9"}, + {file = "kiwisolver-1.4.4-pp38-pypy38_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:81e38381b782cc7e1e46c4e14cd997ee6040768101aefc8fa3c24a4cc58e98f8"}, + {file = "kiwisolver-1.4.4-pp38-pypy38_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:2a66fdfb34e05b705620dd567f5a03f239a088d5a3f321e7b6ac3239d22aa286"}, + {file = "kiwisolver-1.4.4-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:872b8ca05c40d309ed13eb2e582cab0c5a05e81e987ab9c521bf05ad1d5cf5cb"}, + {file = "kiwisolver-1.4.4-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:70e7c2e7b750585569564e2e5ca9845acfaa5da56ac46df68414f29fea97be9f"}, + {file = "kiwisolver-1.4.4-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:9f85003f5dfa867e86d53fac6f7e6f30c045673fa27b603c397753bebadc3008"}, + {file = "kiwisolver-1.4.4-pp39-pypy39_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2e307eb9bd99801f82789b44bb45e9f541961831c7311521b13a6c85afc09767"}, + {file = "kiwisolver-1.4.4-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b1792d939ec70abe76f5054d3f36ed5656021dcad1322d1cc996d4e54165cef9"}, + {file = "kiwisolver-1.4.4-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f6cb459eea32a4e2cf18ba5fcece2dbdf496384413bc1bae15583f19e567f3b2"}, + {file = "kiwisolver-1.4.4-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:36dafec3d6d6088d34e2de6b85f9d8e2324eb734162fba59d2ba9ed7a2043d5b"}, + {file = "kiwisolver-1.4.4.tar.gz", hash = "sha256:d41997519fcba4a1e46eb4a2fe31bc12f0ff957b2b81bac28db24744f333e955"}, +] [[package]] -name = "typing-extensions" -version = "3.7.4.3" -description = "Backported and Experimental Type Hints for Python 3.5+" -category = "dev" +name = "libclang" +version = "15.0.6.1" +description = "Clang Python Bindings, mirrored from the official LLVM repo: https://github.com/llvm/llvm-project/tree/main/clang/bindings/python, to make the installation process easier." +category = "main" optional = false python-versions = "*" +files = [ + {file = "libclang-15.0.6.1-py2.py3-none-macosx_10_9_x86_64.whl", hash = "sha256:8621795e07b87e17fc7aac9f071bc7fe6b52ed6110c0a96a9975d8113c8c2527"}, + {file = "libclang-15.0.6.1-py2.py3-none-manylinux2010_x86_64.whl", hash = "sha256:69b01a23ab543908a661532595daa23cf88bd96d80e41f58ba0eaa6a378fe0d8"}, + {file = "libclang-15.0.6.1-py2.py3-none-manylinux2014_aarch64.whl", hash = "sha256:4a5188184b937132c198ee9de9a8a2316d5fdd1a825398d5ad1a8f5e06f9b40e"}, + {file = "libclang-15.0.6.1-py2.py3-none-manylinux2014_armv7l.whl", hash = "sha256:f7ffa02ac5e586cfffde039dcccc439d88d0feac7d77bf9426d9ba7543d16545"}, + {file = "libclang-15.0.6.1-py2.py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:aaebb6aa1db73bac3a0ac41e57ef78743079eb68728adbf7e80ee917ae171529"}, + {file = "libclang-15.0.6.1-py2.py3-none-win_amd64.whl", hash = "sha256:85afb47630d2070e74b886040ceea1846097ca53cc88d0f1d7751d0f49220028"}, + {file = "libclang-15.0.6.1-py2.py3-none-win_arm64.whl", hash = "sha256:687d8549c110c700fece58dd87727421d0710fdd111aa7eecb01faf8e3f50d4e"}, + {file = "libclang-15.0.6.1.tar.gz", hash = "sha256:a1a8fe038af2962c787c5bac81bfa4b82bb8e279e61e70cc934c10f6e20c73ec"}, +] -[metadata] -lock-version = "1.1" -python-versions = "^3.8" -content-hash = "bbeb398ffdd62164f918b918c592ba24f01eaf08bedfed6515c7136494b12a86" - -[metadata.files] -antlr4-python3-runtime = [ - {file = "antlr4-python3-runtime-4.8.tar.gz", hash = "sha256:15793f5d0512a372b4e7d2284058ad32ce7dd27126b105fb0b2245130445db33"}, +[[package]] +name = "markdown" +version = "3.4.1" +description = "Python implementation of Markdown." +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "Markdown-3.4.1-py3-none-any.whl", hash = "sha256:08fb8465cffd03d10b9dd34a5c3fea908e20391a2a90b88d66362cb05beed186"}, + {file = "Markdown-3.4.1.tar.gz", hash = "sha256:3b809086bb6efad416156e00a0da66fe47618a5d6918dd688f53f40c8e4cfeff"}, ] -appdirs = [ - {file = "appdirs-1.4.4-py2.py3-none-any.whl", hash = "sha256:a841dacd6b99318a741b166adb07e19ee71a274450e68237b4650ca1055ab128"}, - {file = "appdirs-1.4.4.tar.gz", hash = "sha256:7d5d0167b2b1ba821647616af46a749d1c653740dd0d2415100fe26e27afdf41"}, + +[package.dependencies] +importlib-metadata = {version = ">=4.4", markers = "python_version < \"3.10\""} + +[package.extras] +testing = ["coverage", "pyyaml"] + +[[package]] +name = "markupsafe" +version = "2.1.2" +description = "Safely add untrusted strings to HTML/XML markup." +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "MarkupSafe-2.1.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:665a36ae6f8f20a4676b53224e33d456a6f5a72657d9c83c2aa00765072f31f7"}, + {file = "MarkupSafe-2.1.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:340bea174e9761308703ae988e982005aedf427de816d1afe98147668cc03036"}, + {file = "MarkupSafe-2.1.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22152d00bf4a9c7c83960521fc558f55a1adbc0631fbb00a9471e097b19d72e1"}, + {file = "MarkupSafe-2.1.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:28057e985dace2f478e042eaa15606c7efccb700797660629da387eb289b9323"}, + {file = "MarkupSafe-2.1.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ca244fa73f50a800cf8c3ebf7fd93149ec37f5cb9596aa8873ae2c1d23498601"}, + {file = "MarkupSafe-2.1.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:d9d971ec1e79906046aa3ca266de79eac42f1dbf3612a05dc9368125952bd1a1"}, + {file = "MarkupSafe-2.1.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:7e007132af78ea9df29495dbf7b5824cb71648d7133cf7848a2a5dd00d36f9ff"}, + {file = "MarkupSafe-2.1.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:7313ce6a199651c4ed9d7e4cfb4aa56fe923b1adf9af3b420ee14e6d9a73df65"}, + {file = "MarkupSafe-2.1.2-cp310-cp310-win32.whl", hash = "sha256:c4a549890a45f57f1ebf99c067a4ad0cb423a05544accaf2b065246827ed9603"}, + {file = "MarkupSafe-2.1.2-cp310-cp310-win_amd64.whl", hash = "sha256:835fb5e38fd89328e9c81067fd642b3593c33e1e17e2fdbf77f5676abb14a156"}, + {file = "MarkupSafe-2.1.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:2ec4f2d48ae59bbb9d1f9d7efb9236ab81429a764dedca114f5fdabbc3788013"}, + {file = "MarkupSafe-2.1.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:608e7073dfa9e38a85d38474c082d4281f4ce276ac0010224eaba11e929dd53a"}, + {file = "MarkupSafe-2.1.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:65608c35bfb8a76763f37036547f7adfd09270fbdbf96608be2bead319728fcd"}, + {file = "MarkupSafe-2.1.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f2bfb563d0211ce16b63c7cb9395d2c682a23187f54c3d79bfec33e6705473c6"}, + {file = "MarkupSafe-2.1.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:da25303d91526aac3672ee6d49a2f3db2d9502a4a60b55519feb1a4c7714e07d"}, + {file = "MarkupSafe-2.1.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:9cad97ab29dfc3f0249b483412c85c8ef4766d96cdf9dcf5a1e3caa3f3661cf1"}, + {file = "MarkupSafe-2.1.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:085fd3201e7b12809f9e6e9bc1e5c96a368c8523fad5afb02afe3c051ae4afcc"}, + {file = "MarkupSafe-2.1.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:1bea30e9bf331f3fef67e0a3877b2288593c98a21ccb2cf29b74c581a4eb3af0"}, + {file = "MarkupSafe-2.1.2-cp311-cp311-win32.whl", hash = "sha256:7df70907e00c970c60b9ef2938d894a9381f38e6b9db73c5be35e59d92e06625"}, + {file = "MarkupSafe-2.1.2-cp311-cp311-win_amd64.whl", hash = "sha256:e55e40ff0cc8cc5c07996915ad367fa47da6b3fc091fdadca7f5403239c5fec3"}, + {file = "MarkupSafe-2.1.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:a6e40afa7f45939ca356f348c8e23048e02cb109ced1eb8420961b2f40fb373a"}, + {file = "MarkupSafe-2.1.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cf877ab4ed6e302ec1d04952ca358b381a882fbd9d1b07cccbfd61783561f98a"}, + {file = "MarkupSafe-2.1.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:63ba06c9941e46fa389d389644e2d8225e0e3e5ebcc4ff1ea8506dce646f8c8a"}, + {file = "MarkupSafe-2.1.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f1cd098434e83e656abf198f103a8207a8187c0fc110306691a2e94a78d0abb2"}, + {file = "MarkupSafe-2.1.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:55f44b440d491028addb3b88f72207d71eeebfb7b5dbf0643f7c023ae1fba619"}, + {file = "MarkupSafe-2.1.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:a6f2fcca746e8d5910e18782f976489939d54a91f9411c32051b4aab2bd7c513"}, + {file = "MarkupSafe-2.1.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:0b462104ba25f1ac006fdab8b6a01ebbfbce9ed37fd37fd4acd70c67c973e460"}, + {file = "MarkupSafe-2.1.2-cp37-cp37m-win32.whl", hash = "sha256:7668b52e102d0ed87cb082380a7e2e1e78737ddecdde129acadb0eccc5423859"}, + {file = "MarkupSafe-2.1.2-cp37-cp37m-win_amd64.whl", hash = "sha256:6d6607f98fcf17e534162f0709aaad3ab7a96032723d8ac8750ffe17ae5a0666"}, + {file = "MarkupSafe-2.1.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:a806db027852538d2ad7555b203300173dd1b77ba116de92da9afbc3a3be3eed"}, + {file = "MarkupSafe-2.1.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:a4abaec6ca3ad8660690236d11bfe28dfd707778e2442b45addd2f086d6ef094"}, + {file = "MarkupSafe-2.1.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f03a532d7dee1bed20bc4884194a16160a2de9ffc6354b3878ec9682bb623c54"}, + {file = "MarkupSafe-2.1.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4cf06cdc1dda95223e9d2d3c58d3b178aa5dacb35ee7e3bbac10e4e1faacb419"}, + {file = "MarkupSafe-2.1.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:22731d79ed2eb25059ae3df1dfc9cb1546691cc41f4e3130fe6bfbc3ecbbecfa"}, + {file = "MarkupSafe-2.1.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:f8ffb705ffcf5ddd0e80b65ddf7bed7ee4f5a441ea7d3419e861a12eaf41af58"}, + {file = "MarkupSafe-2.1.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:8db032bf0ce9022a8e41a22598eefc802314e81b879ae093f36ce9ddf39ab1ba"}, + {file = "MarkupSafe-2.1.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:2298c859cfc5463f1b64bd55cb3e602528db6fa0f3cfd568d3605c50678f8f03"}, + {file = "MarkupSafe-2.1.2-cp38-cp38-win32.whl", hash = "sha256:50c42830a633fa0cf9e7d27664637532791bfc31c731a87b202d2d8ac40c3ea2"}, + {file = "MarkupSafe-2.1.2-cp38-cp38-win_amd64.whl", hash = "sha256:bb06feb762bade6bf3c8b844462274db0c76acc95c52abe8dbed28ae3d44a147"}, + {file = "MarkupSafe-2.1.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:99625a92da8229df6d44335e6fcc558a5037dd0a760e11d84be2260e6f37002f"}, + {file = "MarkupSafe-2.1.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8bca7e26c1dd751236cfb0c6c72d4ad61d986e9a41bbf76cb445f69488b2a2bd"}, + {file = "MarkupSafe-2.1.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:40627dcf047dadb22cd25ea7ecfe9cbf3bbbad0482ee5920b582f3809c97654f"}, + {file = "MarkupSafe-2.1.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:40dfd3fefbef579ee058f139733ac336312663c6706d1163b82b3003fb1925c4"}, + {file = "MarkupSafe-2.1.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:090376d812fb6ac5f171e5938e82e7f2d7adc2b629101cec0db8b267815c85e2"}, + {file = "MarkupSafe-2.1.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:2e7821bffe00aa6bd07a23913b7f4e01328c3d5cc0b40b36c0bd81d362faeb65"}, + {file = "MarkupSafe-2.1.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:c0a33bc9f02c2b17c3ea382f91b4db0e6cde90b63b296422a939886a7a80de1c"}, + {file = "MarkupSafe-2.1.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:b8526c6d437855442cdd3d87eede9c425c4445ea011ca38d937db299382e6fa3"}, + {file = "MarkupSafe-2.1.2-cp39-cp39-win32.whl", hash = "sha256:137678c63c977754abe9086a3ec011e8fd985ab90631145dfb9294ad09c102a7"}, + {file = "MarkupSafe-2.1.2-cp39-cp39-win_amd64.whl", hash = "sha256:0576fe974b40a400449768941d5d0858cc624e3249dfd1e0c33674e5c7ca7aed"}, + {file = "MarkupSafe-2.1.2.tar.gz", hash = "sha256:abcabc8c2b26036d62d4c746381a6f7cf60aafcc653198ad678306986b09450d"}, ] -argparse = [ - {file = "argparse-1.4.0-py2.py3-none-any.whl", hash = "sha256:c31647edb69fd3d465a847ea3157d37bed1f95f19760b11a47aa91c04b666314"}, - {file = "argparse-1.4.0.tar.gz", hash = "sha256:62b089a55be1d8949cd2bc7e0df0bddb9e028faefc8c32038cc84862aefdd6e4"}, + +[[package]] +name = "matplotlib" +version = "3.7.1" +description = "Python plotting package" +category = "main" +optional = false +python-versions = ">=3.8" +files = [ + {file = "matplotlib-3.7.1-cp310-cp310-macosx_10_12_universal2.whl", hash = "sha256:95cbc13c1fc6844ab8812a525bbc237fa1470863ff3dace7352e910519e194b1"}, + {file = "matplotlib-3.7.1-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:08308bae9e91aca1ec6fd6dda66237eef9f6294ddb17f0d0b3c863169bf82353"}, + {file = "matplotlib-3.7.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:544764ba51900da4639c0f983b323d288f94f65f4024dc40ecb1542d74dc0500"}, + {file = "matplotlib-3.7.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:56d94989191de3fcc4e002f93f7f1be5da476385dde410ddafbb70686acf00ea"}, + {file = "matplotlib-3.7.1-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e99bc9e65901bb9a7ce5e7bb24af03675cbd7c70b30ac670aa263240635999a4"}, + {file = "matplotlib-3.7.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eb7d248c34a341cd4c31a06fd34d64306624c8cd8d0def7abb08792a5abfd556"}, + {file = "matplotlib-3.7.1-cp310-cp310-win32.whl", hash = "sha256:ce463ce590f3825b52e9fe5c19a3c6a69fd7675a39d589e8b5fbe772272b3a24"}, + {file = "matplotlib-3.7.1-cp310-cp310-win_amd64.whl", hash = "sha256:3d7bc90727351fb841e4d8ae620d2d86d8ed92b50473cd2b42ce9186104ecbba"}, + {file = "matplotlib-3.7.1-cp311-cp311-macosx_10_12_universal2.whl", hash = "sha256:770a205966d641627fd5cf9d3cb4b6280a716522cd36b8b284a8eb1581310f61"}, + {file = "matplotlib-3.7.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:f67bfdb83a8232cb7a92b869f9355d677bce24485c460b19d01970b64b2ed476"}, + {file = "matplotlib-3.7.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2bf092f9210e105f414a043b92af583c98f50050559616930d884387d0772aba"}, + {file = "matplotlib-3.7.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:89768d84187f31717349c6bfadc0e0d8c321e8eb34522acec8a67b1236a66332"}, + {file = "matplotlib-3.7.1-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:83111e6388dec67822e2534e13b243cc644c7494a4bb60584edbff91585a83c6"}, + {file = "matplotlib-3.7.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a867bf73a7eb808ef2afbca03bcdb785dae09595fbe550e1bab0cd023eba3de0"}, + {file = "matplotlib-3.7.1-cp311-cp311-win32.whl", hash = "sha256:fbdeeb58c0cf0595efe89c05c224e0a502d1aa6a8696e68a73c3efc6bc354304"}, + {file = "matplotlib-3.7.1-cp311-cp311-win_amd64.whl", hash = "sha256:c0bd19c72ae53e6ab979f0ac6a3fafceb02d2ecafa023c5cca47acd934d10be7"}, + {file = "matplotlib-3.7.1-cp38-cp38-macosx_10_12_universal2.whl", hash = "sha256:6eb88d87cb2c49af00d3bbc33a003f89fd9f78d318848da029383bfc08ecfbfb"}, + {file = "matplotlib-3.7.1-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:cf0e4f727534b7b1457898c4f4ae838af1ef87c359b76dcd5330fa31893a3ac7"}, + {file = "matplotlib-3.7.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:46a561d23b91f30bccfd25429c3c706afe7d73a5cc64ef2dfaf2b2ac47c1a5dc"}, + {file = "matplotlib-3.7.1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:8704726d33e9aa8a6d5215044b8d00804561971163563e6e6591f9dcf64340cc"}, + {file = "matplotlib-3.7.1-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:4cf327e98ecf08fcbb82685acaf1939d3338548620ab8dfa02828706402c34de"}, + {file = "matplotlib-3.7.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:617f14ae9d53292ece33f45cba8503494ee199a75b44de7717964f70637a36aa"}, + {file = "matplotlib-3.7.1-cp38-cp38-win32.whl", hash = "sha256:7c9a4b2da6fac77bcc41b1ea95fadb314e92508bf5493ceff058e727e7ecf5b0"}, + {file = "matplotlib-3.7.1-cp38-cp38-win_amd64.whl", hash = "sha256:14645aad967684e92fc349493fa10c08a6da514b3d03a5931a1bac26e6792bd1"}, + {file = "matplotlib-3.7.1-cp39-cp39-macosx_10_12_universal2.whl", hash = "sha256:81a6b377ea444336538638d31fdb39af6be1a043ca5e343fe18d0f17e098770b"}, + {file = "matplotlib-3.7.1-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:28506a03bd7f3fe59cd3cd4ceb2a8d8a2b1db41afede01f66c42561b9be7b4b7"}, + {file = "matplotlib-3.7.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:8c587963b85ce41e0a8af53b9b2de8dddbf5ece4c34553f7bd9d066148dc719c"}, + {file = "matplotlib-3.7.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8bf26ade3ff0f27668989d98c8435ce9327d24cffb7f07d24ef609e33d582439"}, + {file = "matplotlib-3.7.1-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:def58098f96a05f90af7e92fd127d21a287068202aa43b2a93476170ebd99e87"}, + {file = "matplotlib-3.7.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f883a22a56a84dba3b588696a2b8a1ab0d2c3d41be53264115c71b0a942d8fdb"}, + {file = "matplotlib-3.7.1-cp39-cp39-win32.whl", hash = "sha256:4f99e1b234c30c1e9714610eb0c6d2f11809c9c78c984a613ae539ea2ad2eb4b"}, + {file = "matplotlib-3.7.1-cp39-cp39-win_amd64.whl", hash = "sha256:3ba2af245e36990facf67fde840a760128ddd71210b2ab6406e640188d69d136"}, + {file = "matplotlib-3.7.1-pp38-pypy38_pp73-macosx_10_12_x86_64.whl", hash = "sha256:3032884084f541163f295db8a6536e0abb0db464008fadca6c98aaf84ccf4717"}, + {file = "matplotlib-3.7.1-pp38-pypy38_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3a2cb34336110e0ed8bb4f650e817eed61fa064acbefeb3591f1b33e3a84fd96"}, + {file = "matplotlib-3.7.1-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b867e2f952ed592237a1828f027d332d8ee219ad722345b79a001f49df0936eb"}, + {file = "matplotlib-3.7.1-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:57bfb8c8ea253be947ccb2bc2d1bb3862c2bccc662ad1b4626e1f5e004557042"}, + {file = "matplotlib-3.7.1-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:438196cdf5dc8d39b50a45cb6e3f6274edbcf2254f85fa9b895bf85851c3a613"}, + {file = "matplotlib-3.7.1-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:21e9cff1a58d42e74d01153360de92b326708fb205250150018a52c70f43c290"}, + {file = "matplotlib-3.7.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:75d4725d70b7c03e082bbb8a34639ede17f333d7247f56caceb3801cb6ff703d"}, + {file = "matplotlib-3.7.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:97cc368a7268141afb5690760921765ed34867ffb9655dd325ed207af85c7529"}, + {file = "matplotlib-3.7.1.tar.gz", hash = "sha256:7b73305f25eab4541bd7ee0b96d87e53ae9c9f1823be5659b806cd85786fe882"}, ] -art = [ - {file = "art-5.2-py2.py3-none-any.whl", hash = "sha256:0d289b585a1d36720318383643bde71dc4e10416759486ec800a472091bef1a7"}, - {file = "art-5.2.tar.gz", hash = "sha256:c069b08cd11dec860dab1ce2cc2550ffcf1b76968ff322b4303241e030696ba7"}, + +[package.dependencies] +contourpy = ">=1.0.1" +cycler = ">=0.10" +fonttools = ">=4.22.0" +importlib-resources = {version = ">=3.2.0", markers = "python_version < \"3.10\""} +kiwisolver = ">=1.0.1" +numpy = ">=1.20" +packaging = ">=20.0" +pillow = ">=6.2.0" +pyparsing = ">=2.3.1" +python-dateutil = ">=2.7" + +[[package]] +name = "mccabe" +version = "0.6.1" +description = "McCabe checker, plugin for flake8" +category = "dev" +optional = false +python-versions = "*" +files = [ + {file = "mccabe-0.6.1-py2.py3-none-any.whl", hash = "sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42"}, + {file = "mccabe-0.6.1.tar.gz", hash = "sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f"}, ] -attrs = [ - {file = "attrs-21.2.0-py2.py3-none-any.whl", hash = "sha256:149e90d6d8ac20db7a955ad60cf0e6881a3f20d37096140088356da6c716b0b1"}, - {file = "attrs-21.2.0.tar.gz", hash = "sha256:ef6aaac3ca6cd92904cdd0d83f629a15f18053ec84e6432106f7a4d04ae4f5fb"}, + +[[package]] +name = "multiprocess" +version = "0.70.14" +description = "better multiprocessing and multithreading in python" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "multiprocess-0.70.14-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:560a27540daef4ce8b24ed3cc2496a3c670df66c96d02461a4da67473685adf3"}, + {file = "multiprocess-0.70.14-pp37-pypy37_pp73-manylinux_2_24_i686.whl", hash = "sha256:bfbbfa36f400b81d1978c940616bc77776424e5e34cb0c94974b178d727cfcd5"}, + {file = "multiprocess-0.70.14-pp37-pypy37_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:89fed99553a04ec4f9067031f83a886d7fdec5952005551a896a4b6a59575bb9"}, + {file = "multiprocess-0.70.14-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:40a5e3685462079e5fdee7c6789e3ef270595e1755199f0d50685e72523e1d2a"}, + {file = "multiprocess-0.70.14-pp38-pypy38_pp73-manylinux_2_24_i686.whl", hash = "sha256:44936b2978d3f2648727b3eaeab6d7fa0bedf072dc5207bf35a96d5ee7c004cf"}, + {file = "multiprocess-0.70.14-pp38-pypy38_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:e628503187b5d494bf29ffc52d3e1e57bb770ce7ce05d67c4bbdb3a0c7d3b05f"}, + {file = "multiprocess-0.70.14-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:0d5da0fc84aacb0e4bd69c41b31edbf71b39fe2fb32a54eaedcaea241050855c"}, + {file = "multiprocess-0.70.14-pp39-pypy39_pp73-manylinux_2_24_i686.whl", hash = "sha256:6a7b03a5b98e911a7785b9116805bd782815c5e2bd6c91c6a320f26fd3e7b7ad"}, + {file = "multiprocess-0.70.14-pp39-pypy39_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:cea5bdedd10aace3c660fedeac8b087136b4366d4ee49a30f1ebf7409bce00ae"}, + {file = "multiprocess-0.70.14-py310-none-any.whl", hash = "sha256:7dc1f2f6a1d34894c8a9a013fbc807971e336e7cc3f3ff233e61b9dc679b3b5c"}, + {file = "multiprocess-0.70.14-py37-none-any.whl", hash = "sha256:93a8208ca0926d05cdbb5b9250a604c401bed677579e96c14da3090beb798193"}, + {file = "multiprocess-0.70.14-py38-none-any.whl", hash = "sha256:6725bc79666bbd29a73ca148a0fb5f4ea22eed4a8f22fce58296492a02d18a7b"}, + {file = "multiprocess-0.70.14-py39-none-any.whl", hash = "sha256:63cee628b74a2c0631ef15da5534c8aedbc10c38910b9c8b18dcd327528d1ec7"}, + {file = "multiprocess-0.70.14.tar.gz", hash = "sha256:3eddafc12f2260d27ae03fe6069b12570ab4764ab59a75e81624fac453fbf46a"}, ] -black = [ - {file = "black-20.8b1.tar.gz", hash = "sha256:1c02557aa099101b9d21496f8a914e9ed2222ef70336404eeeac8edba836fbea"}, + +[package.dependencies] +dill = ">=0.3.6" + +[[package]] +name = "mypy" +version = "1.1.1" +description = "Optional static typing for Python" +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "mypy-1.1.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:39c7119335be05630611ee798cc982623b9e8f0cff04a0b48dfc26100e0b97af"}, + {file = "mypy-1.1.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:61bf08362e93b6b12fad3eab68c4ea903a077b87c90ac06c11e3d7a09b56b9c1"}, + {file = "mypy-1.1.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dbb19c9f662e41e474e0cff502b7064a7edc6764f5262b6cd91d698163196799"}, + {file = "mypy-1.1.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:315ac73cc1cce4771c27d426b7ea558fb4e2836f89cb0296cbe056894e3a1f78"}, + {file = "mypy-1.1.1-cp310-cp310-win_amd64.whl", hash = "sha256:5cb14ff9919b7df3538590fc4d4c49a0f84392237cbf5f7a816b4161c061829e"}, + {file = "mypy-1.1.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:26cdd6a22b9b40b2fd71881a8a4f34b4d7914c679f154f43385ca878a8297389"}, + {file = "mypy-1.1.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5b5f81b40d94c785f288948c16e1f2da37203c6006546c5d947aab6f90aefef2"}, + {file = "mypy-1.1.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:21b437be1c02712a605591e1ed1d858aba681757a1e55fe678a15c2244cd68a5"}, + {file = "mypy-1.1.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:d809f88734f44a0d44959d795b1e6f64b2bbe0ea4d9cc4776aa588bb4229fc1c"}, + {file = "mypy-1.1.1-cp311-cp311-win_amd64.whl", hash = "sha256:a380c041db500e1410bb5b16b3c1c35e61e773a5c3517926b81dfdab7582be54"}, + {file = "mypy-1.1.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b7c7b708fe9a871a96626d61912e3f4ddd365bf7f39128362bc50cbd74a634d5"}, + {file = "mypy-1.1.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c1c10fa12df1232c936830839e2e935d090fc9ee315744ac33b8a32216b93707"}, + {file = "mypy-1.1.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:0a28a76785bf57655a8ea5eb0540a15b0e781c807b5aa798bd463779988fa1d5"}, + {file = "mypy-1.1.1-cp37-cp37m-win_amd64.whl", hash = "sha256:ef6a01e563ec6a4940784c574d33f6ac1943864634517984471642908b30b6f7"}, + {file = "mypy-1.1.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d64c28e03ce40d5303450f547e07418c64c241669ab20610f273c9e6290b4b0b"}, + {file = "mypy-1.1.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:64cc3afb3e9e71a79d06e3ed24bb508a6d66f782aff7e56f628bf35ba2e0ba51"}, + {file = "mypy-1.1.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ce61663faf7a8e5ec6f456857bfbcec2901fbdb3ad958b778403f63b9e606a1b"}, + {file = "mypy-1.1.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:2b0c373d071593deefbcdd87ec8db91ea13bd8f1328d44947e88beae21e8d5e9"}, + {file = "mypy-1.1.1-cp38-cp38-win_amd64.whl", hash = "sha256:2888ce4fe5aae5a673386fa232473014056967f3904f5abfcf6367b5af1f612a"}, + {file = "mypy-1.1.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:19ba15f9627a5723e522d007fe708007bae52b93faab00f95d72f03e1afa9598"}, + {file = "mypy-1.1.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:59bbd71e5c58eed2e992ce6523180e03c221dcd92b52f0e792f291d67b15a71c"}, + {file = "mypy-1.1.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9401e33814cec6aec8c03a9548e9385e0e228fc1b8b0a37b9ea21038e64cdd8a"}, + {file = "mypy-1.1.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:4b398d8b1f4fba0e3c6463e02f8ad3346f71956b92287af22c9b12c3ec965a9f"}, + {file = "mypy-1.1.1-cp39-cp39-win_amd64.whl", hash = "sha256:69b35d1dcb5707382810765ed34da9db47e7f95b3528334a3c999b0c90fe523f"}, + {file = "mypy-1.1.1-py3-none-any.whl", hash = "sha256:4e4e8b362cdf99ba00c2b218036002bdcdf1e0de085cdb296a49df03fb31dfc4"}, + {file = "mypy-1.1.1.tar.gz", hash = "sha256:ae9ceae0f5b9059f33dbc62dea087e942c0ccab4b7a003719cb70f9b8abfa32f"}, ] -click = [ - {file = "click-7.1.2-py2.py3-none-any.whl", hash = "sha256:dacca89f4bfadd5de3d7489b7c8a566eee0d3676333fbb50030263894c38c0dc"}, - {file = "click-7.1.2.tar.gz", hash = "sha256:d2b5255c7c6349bc1bd1e59e08cd12acbbd63ce649f2588755783aa94dfb6b1a"}, + +[package.dependencies] +mypy-extensions = ">=1.0.0" +tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} +typing-extensions = ">=3.10" + +[package.extras] +dmypy = ["psutil (>=4.0)"] +install-types = ["pip"] +python2 = ["typed-ast (>=1.4.0,<2)"] +reports = ["lxml"] + +[[package]] +name = "mypy-extensions" +version = "1.0.0" +description = "Type system extensions for programs checked with the mypy type checker." +category = "dev" +optional = false +python-versions = ">=3.5" +files = [ + {file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"}, + {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"}, ] -dafd = [] -decorator = [ - {file = "decorator-4.4.2-py2.py3-none-any.whl", hash = "sha256:41fa54c2a0cc4ba648be4fd43cff00aedf5b9465c9bf18d64325bc225f08f760"}, - {file = "decorator-4.4.2.tar.gz", hash = "sha256:e3a62f0520172440ca0dcc823749319382e377f37f140a0b99ef45fecb84bfe7"}, + +[[package]] +name = "networkx" +version = "3.0" +description = "Python package for creating and manipulating graphs and networks" +category = "main" +optional = false +python-versions = ">=3.8" +files = [ + {file = "networkx-3.0-py3-none-any.whl", hash = "sha256:58058d66b1818043527244fab9d41a51fcd7dcc271748015f3c181b8a90c8e2e"}, + {file = "networkx-3.0.tar.gz", hash = "sha256:9a9992345353618ae98339c2b63d8201c381c2944f38a2ab49cb45a4c667e412"}, ] -flake8 = [ - {file = "flake8-3.8.4-py2.py3-none-any.whl", hash = "sha256:749dbbd6bfd0cf1318af27bf97a14e28e5ff548ef8e5b1566ccfb25a11e7c839"}, - {file = "flake8-3.8.4.tar.gz", hash = "sha256:aadae8761ec651813c24be05c6f7b4680857ef6afaae4651a4eccaef97ce6c3b"}, + +[package.extras] +default = ["matplotlib (>=3.4)", "numpy (>=1.20)", "pandas (>=1.3)", "scipy (>=1.8)"] +developer = ["mypy (>=0.991)", "pre-commit (>=2.20)"] +doc = ["nb2plots (>=0.6)", "numpydoc (>=1.5)", "pillow (>=9.2)", "pydata-sphinx-theme (>=0.11)", "sphinx (==5.2.3)", "sphinx-gallery (>=0.11)", "texext (>=0.6.7)"] +extra = ["lxml (>=4.6)", "pydot (>=1.4.2)", "pygraphviz (>=1.10)", "sympy (>=1.10)"] +test = ["codecov (>=2.1)", "pytest (>=7.2)", "pytest-cov (>=4.0)"] + +[[package]] +name = "numpy" +version = "1.24.2" +description = "Fundamental package for array computing in Python" +category = "main" +optional = false +python-versions = ">=3.8" +files = [ + {file = "numpy-1.24.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:eef70b4fc1e872ebddc38cddacc87c19a3709c0e3e5d20bf3954c147b1dd941d"}, + {file = "numpy-1.24.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e8d2859428712785e8a8b7d2b3ef0a1d1565892367b32f915c4a4df44d0e64f5"}, + {file = "numpy-1.24.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6524630f71631be2dabe0c541e7675db82651eb998496bbe16bc4f77f0772253"}, + {file = "numpy-1.24.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a51725a815a6188c662fb66fb32077709a9ca38053f0274640293a14fdd22978"}, + {file = "numpy-1.24.2-cp310-cp310-win32.whl", hash = "sha256:2620e8592136e073bd12ee4536149380695fbe9ebeae845b81237f986479ffc9"}, + {file = "numpy-1.24.2-cp310-cp310-win_amd64.whl", hash = "sha256:97cf27e51fa078078c649a51d7ade3c92d9e709ba2bfb97493007103c741f1d0"}, + {file = "numpy-1.24.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:7de8fdde0003f4294655aa5d5f0a89c26b9f22c0a58790c38fae1ed392d44a5a"}, + {file = "numpy-1.24.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4173bde9fa2a005c2c6e2ea8ac1618e2ed2c1c6ec8a7657237854d42094123a0"}, + {file = "numpy-1.24.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4cecaed30dc14123020f77b03601559fff3e6cd0c048f8b5289f4eeabb0eb281"}, + {file = "numpy-1.24.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9a23f8440561a633204a67fb44617ce2a299beecf3295f0d13c495518908e910"}, + {file = "numpy-1.24.2-cp311-cp311-win32.whl", hash = "sha256:e428c4fbfa085f947b536706a2fc349245d7baa8334f0c5723c56a10595f9b95"}, + {file = "numpy-1.24.2-cp311-cp311-win_amd64.whl", hash = "sha256:557d42778a6869c2162deb40ad82612645e21d79e11c1dc62c6e82a2220ffb04"}, + {file = "numpy-1.24.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d0a2db9d20117bf523dde15858398e7c0858aadca7c0f088ac0d6edd360e9ad2"}, + {file = "numpy-1.24.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:c72a6b2f4af1adfe193f7beb91ddf708ff867a3f977ef2ec53c0ffb8283ab9f5"}, + {file = "numpy-1.24.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c29e6bd0ec49a44d7690ecb623a8eac5ab8a923bce0bea6293953992edf3a76a"}, + {file = "numpy-1.24.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2eabd64ddb96a1239791da78fa5f4e1693ae2dadc82a76bc76a14cbb2b966e96"}, + {file = "numpy-1.24.2-cp38-cp38-win32.whl", hash = "sha256:e3ab5d32784e843fc0dd3ab6dcafc67ef806e6b6828dc6af2f689be0eb4d781d"}, + {file = "numpy-1.24.2-cp38-cp38-win_amd64.whl", hash = "sha256:76807b4063f0002c8532cfeac47a3068a69561e9c8715efdad3c642eb27c0756"}, + {file = "numpy-1.24.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:4199e7cfc307a778f72d293372736223e39ec9ac096ff0a2e64853b866a8e18a"}, + {file = "numpy-1.24.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:adbdce121896fd3a17a77ab0b0b5eedf05a9834a18699db6829a64e1dfccca7f"}, + {file = "numpy-1.24.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:889b2cc88b837d86eda1b17008ebeb679d82875022200c6e8e4ce6cf549b7acb"}, + {file = "numpy-1.24.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f64bb98ac59b3ea3bf74b02f13836eb2e24e48e0ab0145bbda646295769bd780"}, + {file = "numpy-1.24.2-cp39-cp39-win32.whl", hash = "sha256:63e45511ee4d9d976637d11e6c9864eae50e12dc9598f531c035265991910468"}, + {file = "numpy-1.24.2-cp39-cp39-win_amd64.whl", hash = "sha256:a77d3e1163a7770164404607b7ba3967fb49b24782a6ef85d9b5f54126cc39e5"}, + {file = "numpy-1.24.2-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:92011118955724465fb6853def593cf397b4a1367495e0b59a7e69d40c4eb71d"}, + {file = "numpy-1.24.2-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f9006288bcf4895917d02583cf3411f98631275bc67cce355a7f39f8c14338fa"}, + {file = "numpy-1.24.2-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:150947adbdfeceec4e5926d956a06865c1c690f2fd902efede4ca6fe2e657c3f"}, + {file = "numpy-1.24.2.tar.gz", hash = "sha256:003a9f530e880cb2cd177cba1af7220b9aa42def9c4afc2a2fc3ee6be7eb2b22"}, ] -install = [ - {file = "install-1.3.4-py3-none-any.whl", hash = "sha256:dfec614e0cc90f00659067a078bc2ec032d9f0758f3df230c073eeaffd0cefa1"}, - {file = "install-1.3.4.tar.gz", hash = "sha256:1455304a2475b6753a9b0c28243ca7d8b7d71aeea1a88e6de94034fc57d765a0"}, + +[[package]] +name = "oauthlib" +version = "3.2.2" +description = "A generic, spec-compliant, thorough implementation of the OAuth request-signing logic" +category = "main" +optional = false +python-versions = ">=3.6" +files = [ + {file = "oauthlib-3.2.2-py3-none-any.whl", hash = "sha256:8139f29aac13e25d502680e9e19963e83f16838d48a0d71c287fe40e7067fbca"}, + {file = "oauthlib-3.2.2.tar.gz", hash = "sha256:9859c40929662bec5d64f34d01c99e093149682a3f38915dc0655d5a633dd918"}, ] -jsonschema = [ - {file = "jsonschema-3.2.0-py2.py3-none-any.whl", hash = "sha256:4e5b3cf8216f577bee9ce139cbe72eca3ea4f292ec60928ff24758ce626cd163"}, - {file = "jsonschema-3.2.0.tar.gz", hash = "sha256:c8a85b28d377cc7737e46e2d9f2b4f44ee3c0e1deac6bf46ddefc7187d30797a"}, + +[package.extras] +rsa = ["cryptography (>=3.0.0)"] +signals = ["blinker (>=1.4.0)"] +signedtoken = ["cryptography (>=3.0.0)", "pyjwt (>=2.0.0,<3)"] + +[[package]] +name = "opt-einsum" +version = "3.3.0" +description = "Optimizing numpys einsum function" +category = "main" +optional = false +python-versions = ">=3.5" +files = [ + {file = "opt_einsum-3.3.0-py3-none-any.whl", hash = "sha256:2455e59e3947d3c275477df7f5205b30635e266fe6dc300e3d9f9646bfcea147"}, + {file = "opt_einsum-3.3.0.tar.gz", hash = "sha256:59f6475f77bbc37dcf7cd748519c0ec60722e91e63ca114e68821c0c54a46549"}, ] -mccabe = [ - {file = "mccabe-0.6.1-py2.py3-none-any.whl", hash = "sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42"}, - {file = "mccabe-0.6.1.tar.gz", hash = "sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f"}, + +[package.dependencies] +numpy = ">=1.7" + +[package.extras] +docs = ["numpydoc", "sphinx (==1.2.3)", "sphinx-rtd-theme", "sphinxcontrib-napoleon"] +tests = ["pytest", "pytest-cov", "pytest-pep8"] + +[[package]] +name = "packaging" +version = "23.0" +description = "Core utilities for Python packages" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "packaging-23.0-py3-none-any.whl", hash = "sha256:714ac14496c3e68c99c29b00845f7a2b85f3bb6f1078fd9f72fd20f0570002b2"}, + {file = "packaging-23.0.tar.gz", hash = "sha256:b6ad297f8907de0fa2fe1ccbd26fdaf387f5f47c7275fedf8cce89f99446cf97"}, ] -mypy = [ - {file = "mypy-0.782-cp35-cp35m-macosx_10_6_x86_64.whl", hash = "sha256:2c6cde8aa3426c1682d35190b59b71f661237d74b053822ea3d748e2c9578a7c"}, - {file = "mypy-0.782-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:9c7a9a7ceb2871ba4bac1cf7217a7dd9ccd44c27c2950edbc6dc08530f32ad4e"}, - {file = "mypy-0.782-cp35-cp35m-win_amd64.whl", hash = "sha256:c05b9e4fb1d8a41d41dec8786c94f3b95d3c5f528298d769eb8e73d293abc48d"}, - {file = "mypy-0.782-cp36-cp36m-macosx_10_6_x86_64.whl", hash = "sha256:6731603dfe0ce4352c555c6284c6db0dc935b685e9ce2e4cf220abe1e14386fd"}, - {file = "mypy-0.782-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:f05644db6779387ccdb468cc47a44b4356fc2ffa9287135d05b70a98dc83b89a"}, - {file = "mypy-0.782-cp36-cp36m-win_amd64.whl", hash = "sha256:b7fbfabdbcc78c4f6fc4712544b9b0d6bf171069c6e0e3cb82440dd10ced3406"}, - {file = "mypy-0.782-cp37-cp37m-macosx_10_6_x86_64.whl", hash = "sha256:3fdda71c067d3ddfb21da4b80e2686b71e9e5c72cca65fa216d207a358827f86"}, - {file = "mypy-0.782-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:d7df6eddb6054d21ca4d3c6249cae5578cb4602951fd2b6ee2f5510ffb098707"}, - {file = "mypy-0.782-cp37-cp37m-win_amd64.whl", hash = "sha256:a4a2cbcfc4cbf45cd126f531dedda8485671545b43107ded25ce952aac6fb308"}, - {file = "mypy-0.782-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6bb93479caa6619d21d6e7160c552c1193f6952f0668cdda2f851156e85186fc"}, - {file = "mypy-0.782-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:81c7908b94239c4010e16642c9102bfc958ab14e36048fa77d0be3289dda76ea"}, - {file = "mypy-0.782-cp38-cp38-win_amd64.whl", hash = "sha256:5dd13ff1f2a97f94540fd37a49e5d255950ebcdf446fb597463a40d0df3fac8b"}, - {file = "mypy-0.782-py3-none-any.whl", hash = "sha256:e0b61738ab504e656d1fe4ff0c0601387a5489ca122d55390ade31f9ca0e252d"}, - {file = "mypy-0.782.tar.gz", hash = "sha256:eff7d4a85e9eea55afa34888dfeaccde99e7520b51f867ac28a48492c0b1130c"}, -] -mypy-extensions = [ - {file = "mypy_extensions-0.4.3-py2.py3-none-any.whl", hash = "sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d"}, - {file = "mypy_extensions-0.4.3.tar.gz", hash = "sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8"}, -] -networkx = [ - {file = "networkx-2.5-py3-none-any.whl", hash = "sha256:8c5812e9f798d37c50570d15c4a69d5710a18d77bafc903ee9c5fba7454c616c"}, - {file = "networkx-2.5.tar.gz", hash = "sha256:7978955423fbc9639c10498878be59caf99b44dc304c2286162fd24b458c1602"}, + +[[package]] +name = "pandas" +version = "1.5.3" +description = "Powerful data structures for data analysis, time series, and statistics" +category = "main" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pandas-1.5.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:3749077d86e3a2f0ed51367f30bf5b82e131cc0f14260c4d3e499186fccc4406"}, + {file = "pandas-1.5.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:972d8a45395f2a2d26733eb8d0f629b2f90bebe8e8eddbb8829b180c09639572"}, + {file = "pandas-1.5.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:50869a35cbb0f2e0cd5ec04b191e7b12ed688874bd05dd777c19b28cbea90996"}, + {file = "pandas-1.5.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c3ac844a0fe00bfaeb2c9b51ab1424e5c8744f89860b138434a363b1f620f354"}, + {file = "pandas-1.5.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7a0a56cef15fd1586726dace5616db75ebcfec9179a3a55e78f72c5639fa2a23"}, + {file = "pandas-1.5.3-cp310-cp310-win_amd64.whl", hash = "sha256:478ff646ca42b20376e4ed3fa2e8d7341e8a63105586efe54fa2508ee087f328"}, + {file = "pandas-1.5.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:6973549c01ca91ec96199e940495219c887ea815b2083722821f1d7abfa2b4dc"}, + {file = "pandas-1.5.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c39a8da13cede5adcd3be1182883aea1c925476f4e84b2807a46e2775306305d"}, + {file = "pandas-1.5.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f76d097d12c82a535fda9dfe5e8dd4127952b45fea9b0276cb30cca5ea313fbc"}, + {file = "pandas-1.5.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e474390e60ed609cec869b0da796ad94f420bb057d86784191eefc62b65819ae"}, + {file = "pandas-1.5.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5f2b952406a1588ad4cad5b3f55f520e82e902388a6d5a4a91baa8d38d23c7f6"}, + {file = "pandas-1.5.3-cp311-cp311-win_amd64.whl", hash = "sha256:bc4c368f42b551bf72fac35c5128963a171b40dce866fb066540eeaf46faa003"}, + {file = "pandas-1.5.3-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:14e45300521902689a81f3f41386dc86f19b8ba8dd5ac5a3c7010ef8d2932813"}, + {file = "pandas-1.5.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:9842b6f4b8479e41968eced654487258ed81df7d1c9b7b870ceea24ed9459b31"}, + {file = "pandas-1.5.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:26d9c71772c7afb9d5046e6e9cf42d83dd147b5cf5bcb9d97252077118543792"}, + {file = "pandas-1.5.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5fbcb19d6fceb9e946b3e23258757c7b225ba450990d9ed63ccceeb8cae609f7"}, + {file = "pandas-1.5.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:565fa34a5434d38e9d250af3c12ff931abaf88050551d9fbcdfafca50d62babf"}, + {file = "pandas-1.5.3-cp38-cp38-win32.whl", hash = "sha256:87bd9c03da1ac870a6d2c8902a0e1fd4267ca00f13bc494c9e5a9020920e1d51"}, + {file = "pandas-1.5.3-cp38-cp38-win_amd64.whl", hash = "sha256:41179ce559943d83a9b4bbacb736b04c928b095b5f25dd2b7389eda08f46f373"}, + {file = "pandas-1.5.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:c74a62747864ed568f5a82a49a23a8d7fe171d0c69038b38cedf0976831296fa"}, + {file = "pandas-1.5.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c4c00e0b0597c8e4f59e8d461f797e5d70b4d025880516a8261b2817c47759ee"}, + {file = "pandas-1.5.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a50d9a4336a9621cab7b8eb3fb11adb82de58f9b91d84c2cd526576b881a0c5a"}, + {file = "pandas-1.5.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dd05f7783b3274aa206a1af06f0ceed3f9b412cf665b7247eacd83be41cf7bf0"}, + {file = "pandas-1.5.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9f69c4029613de47816b1bb30ff5ac778686688751a5e9c99ad8c7031f6508e5"}, + {file = "pandas-1.5.3-cp39-cp39-win32.whl", hash = "sha256:7cec0bee9f294e5de5bbfc14d0573f65526071029d036b753ee6507d2a21480a"}, + {file = "pandas-1.5.3-cp39-cp39-win_amd64.whl", hash = "sha256:dfd681c5dc216037e0b0a2c821f5ed99ba9f03ebcf119c7dac0e9a7b960b9ec9"}, + {file = "pandas-1.5.3.tar.gz", hash = "sha256:74a3fd7e5a7ec052f183273dc7b0acd3a863edf7520f5d3a1765c04ffdb3b0b1"}, ] + +[package.dependencies] numpy = [ - {file = "numpy-1.19.5-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:cc6bd4fd593cb261332568485e20a0712883cf631f6f5e8e86a52caa8b2b50ff"}, - {file = "numpy-1.19.5-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:aeb9ed923be74e659984e321f609b9ba54a48354bfd168d21a2b072ed1e833ea"}, - {file = "numpy-1.19.5-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:8b5e972b43c8fc27d56550b4120fe6257fdc15f9301914380b27f74856299fea"}, - {file = "numpy-1.19.5-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:43d4c81d5ffdff6bae58d66a3cd7f54a7acd9a0e7b18d97abb255defc09e3140"}, - {file = "numpy-1.19.5-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:a4646724fba402aa7504cd48b4b50e783296b5e10a524c7a6da62e4a8ac9698d"}, - {file = "numpy-1.19.5-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:2e55195bc1c6b705bfd8ad6f288b38b11b1af32f3c8289d6c50d47f950c12e76"}, - {file = "numpy-1.19.5-cp36-cp36m-win32.whl", hash = "sha256:39b70c19ec771805081578cc936bbe95336798b7edf4732ed102e7a43ec5c07a"}, - {file = "numpy-1.19.5-cp36-cp36m-win_amd64.whl", hash = "sha256:dbd18bcf4889b720ba13a27ec2f2aac1981bd41203b3a3b27ba7a33f88ae4827"}, - {file = "numpy-1.19.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:603aa0706be710eea8884af807b1b3bc9fb2e49b9f4da439e76000f3b3c6ff0f"}, - {file = "numpy-1.19.5-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:cae865b1cae1ec2663d8ea56ef6ff185bad091a5e33ebbadd98de2cfa3fa668f"}, - {file = "numpy-1.19.5-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:36674959eed6957e61f11c912f71e78857a8d0604171dfd9ce9ad5cbf41c511c"}, - {file = "numpy-1.19.5-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:06fab248a088e439402141ea04f0fffb203723148f6ee791e9c75b3e9e82f080"}, - {file = "numpy-1.19.5-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:6149a185cece5ee78d1d196938b2a8f9d09f5a5ebfbba66969302a778d5ddd1d"}, - {file = "numpy-1.19.5-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:50a4a0ad0111cc1b71fa32dedd05fa239f7fb5a43a40663269bb5dc7877cfd28"}, - {file = "numpy-1.19.5-cp37-cp37m-win32.whl", hash = "sha256:d051ec1c64b85ecc69531e1137bb9751c6830772ee5c1c426dbcfe98ef5788d7"}, - {file = "numpy-1.19.5-cp37-cp37m-win_amd64.whl", hash = "sha256:a12ff4c8ddfee61f90a1633a4c4afd3f7bcb32b11c52026c92a12e1325922d0d"}, - {file = "numpy-1.19.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:cf2402002d3d9f91c8b01e66fbb436a4ed01c6498fffed0e4c7566da1d40ee1e"}, - {file = "numpy-1.19.5-cp38-cp38-manylinux1_i686.whl", hash = "sha256:1ded4fce9cfaaf24e7a0ab51b7a87be9038ea1ace7f34b841fe3b6894c721d1c"}, - {file = "numpy-1.19.5-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:012426a41bc9ab63bb158635aecccc7610e3eff5d31d1eb43bc099debc979d94"}, - {file = "numpy-1.19.5-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:759e4095edc3c1b3ac031f34d9459fa781777a93ccc633a472a5468587a190ff"}, - {file = "numpy-1.19.5-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:a9d17f2be3b427fbb2bce61e596cf555d6f8a56c222bd2ca148baeeb5e5c783c"}, - {file = "numpy-1.19.5-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:99abf4f353c3d1a0c7a5f27699482c987cf663b1eac20db59b8c7b061eabd7fc"}, - {file = "numpy-1.19.5-cp38-cp38-win32.whl", hash = "sha256:384ec0463d1c2671170901994aeb6dce126de0a95ccc3976c43b0038a37329c2"}, - {file = "numpy-1.19.5-cp38-cp38-win_amd64.whl", hash = "sha256:811daee36a58dc79cf3d8bdd4a490e4277d0e4b7d103a001a4e73ddb48e7e6aa"}, - {file = "numpy-1.19.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c843b3f50d1ab7361ca4f0b3639bf691569493a56808a0b0c54a051d260b7dbd"}, - {file = "numpy-1.19.5-cp39-cp39-manylinux1_i686.whl", hash = "sha256:d6631f2e867676b13026e2846180e2c13c1e11289d67da08d71cacb2cd93d4aa"}, - {file = "numpy-1.19.5-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:7fb43004bce0ca31d8f13a6eb5e943fa73371381e53f7074ed21a4cb786c32f8"}, - {file = "numpy-1.19.5-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:2ea52bd92ab9f768cc64a4c3ef8f4b2580a17af0a5436f6126b08efbd1838371"}, - {file = "numpy-1.19.5-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:400580cbd3cff6ffa6293df2278c75aef2d58d8d93d3c5614cd67981dae68ceb"}, - {file = "numpy-1.19.5-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:df609c82f18c5b9f6cb97271f03315ff0dbe481a2a02e56aeb1b1a985ce38e60"}, - {file = "numpy-1.19.5-cp39-cp39-win32.whl", hash = "sha256:ab83f24d5c52d60dbc8cd0528759532736b56db58adaa7b5f1f76ad551416a1e"}, - {file = "numpy-1.19.5-cp39-cp39-win_amd64.whl", hash = "sha256:0eef32ca3132a48e43f6a0f5a82cb508f22ce5a3d6f67a8329c81c8e226d3f6e"}, - {file = "numpy-1.19.5-pp36-pypy36_pp73-manylinux2010_x86_64.whl", hash = "sha256:a0d53e51a6cb6f0d9082decb7a4cb6dfb33055308c4c44f53103c073f649af73"}, - {file = "numpy-1.19.5.zip", hash = "sha256:a76f502430dd98d7546e1ea2250a7360c065a5fdea52b2dffe8ae7180909b6f4"}, -] -parchmint = [] -pathspec = [ - {file = "pathspec-0.8.1-py2.py3-none-any.whl", hash = "sha256:aa0cb481c4041bf52ffa7b0d8fa6cd3e88a2ca4879c533c9153882ee2556790d"}, - {file = "pathspec-0.8.1.tar.gz", hash = "sha256:86379d6b86d75816baba717e64b1a3a3469deb93bb76d613c9ce79edc5cb68fd"}, -] -pycodestyle = [ - {file = "pycodestyle-2.6.0-py2.py3-none-any.whl", hash = "sha256:2295e7b2f6b5bd100585ebcb1f616591b652db8a741695b3d8f5d28bdc934367"}, - {file = "pycodestyle-2.6.0.tar.gz", hash = "sha256:c58a7d2815e0e8d7972bf1803331fb0152f867bd89adf8a01dfd55085434192e"}, -] -pyeda = [ + {version = ">=1.20.3", markers = "python_version < \"3.10\""}, + {version = ">=1.21.0", markers = "python_version >= \"3.10\""}, +] +python-dateutil = ">=2.8.1" +pytz = ">=2020.1" + +[package.extras] +test = ["hypothesis (>=5.5.3)", "pytest (>=6.0)", "pytest-xdist (>=1.31)"] + +[[package]] +name = "parchmint" +version = "0.3.3" +description = "Parchmint is data interchange standard for representing microfluidic designs. Check out https://parchmint.org for more information." +category = "main" +optional = false +python-versions = ">=3.8,<3.11" +files = [] +develop = true + +[package.dependencies] +argparse = "^1.4.0" +jsonschema = "^3.2.0" +networkx = "^3.0" +numpy = "^1.22.4" +tabulate = "^0.8.9" + +[package.extras] +docs = [] + +[package.source] +type = "directory" +url = "pyparchmint" + +[[package]] +name = "pathspec" +version = "0.11.0" +description = "Utility library for gitignore style pattern matching of file paths." +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "pathspec-0.11.0-py3-none-any.whl", hash = "sha256:3a66eb970cbac598f9e5ccb5b2cf58930cd8e3ed86d393d541eaf2d8b1705229"}, + {file = "pathspec-0.11.0.tar.gz", hash = "sha256:64d338d4e0914e91c1792321e6907b5a593f1ab1851de7fc269557a21b30ebbc"}, +] + +[[package]] +name = "pillow" +version = "9.4.0" +description = "Python Imaging Library (Fork)" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "Pillow-9.4.0-1-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:1b4b4e9dda4f4e4c4e6896f93e84a8f0bcca3b059de9ddf67dac3c334b1195e1"}, + {file = "Pillow-9.4.0-1-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:fb5c1ad6bad98c57482236a21bf985ab0ef42bd51f7ad4e4538e89a997624e12"}, + {file = "Pillow-9.4.0-1-cp37-cp37m-macosx_10_10_x86_64.whl", hash = "sha256:f0caf4a5dcf610d96c3bd32932bfac8aee61c96e60481c2a0ea58da435e25acd"}, + {file = "Pillow-9.4.0-1-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:3f4cc516e0b264c8d4ccd6b6cbc69a07c6d582d8337df79be1e15a5056b258c9"}, + {file = "Pillow-9.4.0-1-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:b8c2f6eb0df979ee99433d8b3f6d193d9590f735cf12274c108bd954e30ca858"}, + {file = "Pillow-9.4.0-1-pp38-pypy38_pp73-macosx_10_10_x86_64.whl", hash = "sha256:b70756ec9417c34e097f987b4d8c510975216ad26ba6e57ccb53bc758f490dab"}, + {file = "Pillow-9.4.0-1-pp39-pypy39_pp73-macosx_10_10_x86_64.whl", hash = "sha256:43521ce2c4b865d385e78579a082b6ad1166ebed2b1a2293c3be1d68dd7ca3b9"}, + {file = "Pillow-9.4.0-2-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:9d9a62576b68cd90f7075876f4e8444487db5eeea0e4df3ba298ee38a8d067b0"}, + {file = "Pillow-9.4.0-2-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:87708d78a14d56a990fbf4f9cb350b7d89ee8988705e58e39bdf4d82c149210f"}, + {file = "Pillow-9.4.0-2-cp37-cp37m-macosx_10_10_x86_64.whl", hash = "sha256:8a2b5874d17e72dfb80d917213abd55d7e1ed2479f38f001f264f7ce7bae757c"}, + {file = "Pillow-9.4.0-2-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:83125753a60cfc8c412de5896d10a0a405e0bd88d0470ad82e0869ddf0cb3848"}, + {file = "Pillow-9.4.0-2-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:9e5f94742033898bfe84c93c831a6f552bb629448d4072dd312306bab3bd96f1"}, + {file = "Pillow-9.4.0-2-pp38-pypy38_pp73-macosx_10_10_x86_64.whl", hash = "sha256:013016af6b3a12a2f40b704677f8b51f72cb007dac785a9933d5c86a72a7fe33"}, + {file = "Pillow-9.4.0-2-pp39-pypy39_pp73-macosx_10_10_x86_64.whl", hash = "sha256:99d92d148dd03fd19d16175b6d355cc1b01faf80dae93c6c3eb4163709edc0a9"}, + {file = "Pillow-9.4.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:2968c58feca624bb6c8502f9564dd187d0e1389964898f5e9e1fbc8533169157"}, + {file = "Pillow-9.4.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c5c1362c14aee73f50143d74389b2c158707b4abce2cb055b7ad37ce60738d47"}, + {file = "Pillow-9.4.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bd752c5ff1b4a870b7661234694f24b1d2b9076b8bf337321a814c612665f343"}, + {file = "Pillow-9.4.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9a3049a10261d7f2b6514d35bbb7a4dfc3ece4c4de14ef5876c4b7a23a0e566d"}, + {file = "Pillow-9.4.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:16a8df99701f9095bea8a6c4b3197da105df6f74e6176c5b410bc2df2fd29a57"}, + {file = "Pillow-9.4.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:94cdff45173b1919350601f82d61365e792895e3c3a3443cf99819e6fbf717a5"}, + {file = "Pillow-9.4.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:ed3e4b4e1e6de75fdc16d3259098de7c6571b1a6cc863b1a49e7d3d53e036070"}, + {file = "Pillow-9.4.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:d5b2f8a31bd43e0f18172d8ac82347c8f37ef3e0b414431157718aa234991b28"}, + {file = "Pillow-9.4.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:09b89ddc95c248ee788328528e6a2996e09eaccddeeb82a5356e92645733be35"}, + {file = "Pillow-9.4.0-cp310-cp310-win32.whl", hash = "sha256:f09598b416ba39a8f489c124447b007fe865f786a89dbfa48bb5cf395693132a"}, + {file = "Pillow-9.4.0-cp310-cp310-win_amd64.whl", hash = "sha256:f6e78171be3fb7941f9910ea15b4b14ec27725865a73c15277bc39f5ca4f8391"}, + {file = "Pillow-9.4.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:3fa1284762aacca6dc97474ee9c16f83990b8eeb6697f2ba17140d54b453e133"}, + {file = "Pillow-9.4.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:eaef5d2de3c7e9b21f1e762f289d17b726c2239a42b11e25446abf82b26ac132"}, + {file = "Pillow-9.4.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a4dfdae195335abb4e89cc9762b2edc524f3c6e80d647a9a81bf81e17e3fb6f0"}, + {file = "Pillow-9.4.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6abfb51a82e919e3933eb137e17c4ae9c0475a25508ea88993bb59faf82f3b35"}, + {file = "Pillow-9.4.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:451f10ef963918e65b8869e17d67db5e2f4ab40e716ee6ce7129b0cde2876eab"}, + {file = "Pillow-9.4.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:6663977496d616b618b6cfa43ec86e479ee62b942e1da76a2c3daa1c75933ef4"}, + {file = "Pillow-9.4.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:60e7da3a3ad1812c128750fc1bc14a7ceeb8d29f77e0a2356a8fb2aa8925287d"}, + {file = "Pillow-9.4.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:19005a8e58b7c1796bc0167862b1f54a64d3b44ee5d48152b06bb861458bc0f8"}, + {file = "Pillow-9.4.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:f715c32e774a60a337b2bb8ad9839b4abf75b267a0f18806f6f4f5f1688c4b5a"}, + {file = "Pillow-9.4.0-cp311-cp311-win32.whl", hash = "sha256:b222090c455d6d1a64e6b7bb5f4035c4dff479e22455c9eaa1bdd4c75b52c80c"}, + {file = "Pillow-9.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:ba6612b6548220ff5e9df85261bddc811a057b0b465a1226b39bfb8550616aee"}, + {file = "Pillow-9.4.0-cp37-cp37m-macosx_10_10_x86_64.whl", hash = "sha256:5f532a2ad4d174eb73494e7397988e22bf427f91acc8e6ebf5bb10597b49c493"}, + {file = "Pillow-9.4.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5dd5a9c3091a0f414a963d427f920368e2b6a4c2f7527fdd82cde8ef0bc7a327"}, + {file = "Pillow-9.4.0-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ef21af928e807f10bf4141cad4746eee692a0dd3ff56cfb25fce076ec3cc8abe"}, + {file = "Pillow-9.4.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:847b114580c5cc9ebaf216dd8c8dbc6b00a3b7ab0131e173d7120e6deade1f57"}, + {file = "Pillow-9.4.0-cp37-cp37m-manylinux_2_28_aarch64.whl", hash = "sha256:653d7fb2df65efefbcbf81ef5fe5e5be931f1ee4332c2893ca638c9b11a409c4"}, + {file = "Pillow-9.4.0-cp37-cp37m-manylinux_2_28_x86_64.whl", hash = "sha256:46f39cab8bbf4a384ba7cb0bc8bae7b7062b6a11cfac1ca4bc144dea90d4a9f5"}, + {file = "Pillow-9.4.0-cp37-cp37m-win32.whl", hash = "sha256:7ac7594397698f77bce84382929747130765f66406dc2cd8b4ab4da68ade4c6e"}, + {file = "Pillow-9.4.0-cp37-cp37m-win_amd64.whl", hash = "sha256:46c259e87199041583658457372a183636ae8cd56dbf3f0755e0f376a7f9d0e6"}, + {file = "Pillow-9.4.0-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:0e51f608da093e5d9038c592b5b575cadc12fd748af1479b5e858045fff955a9"}, + {file = "Pillow-9.4.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:765cb54c0b8724a7c12c55146ae4647e0274a839fb6de7bcba841e04298e1011"}, + {file = "Pillow-9.4.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:519e14e2c49fcf7616d6d2cfc5c70adae95682ae20f0395e9280db85e8d6c4df"}, + {file = "Pillow-9.4.0-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d197df5489004db87d90b918033edbeee0bd6df3848a204bca3ff0a903bef837"}, + {file = "Pillow-9.4.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0845adc64fe9886db00f5ab68c4a8cd933ab749a87747555cec1c95acea64b0b"}, + {file = "Pillow-9.4.0-cp38-cp38-manylinux_2_28_aarch64.whl", hash = "sha256:e1339790c083c5a4de48f688b4841f18df839eb3c9584a770cbd818b33e26d5d"}, + {file = "Pillow-9.4.0-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:a96e6e23f2b79433390273eaf8cc94fec9c6370842e577ab10dabdcc7ea0a66b"}, + {file = "Pillow-9.4.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:7cfc287da09f9d2a7ec146ee4d72d6ea1342e770d975e49a8621bf54eaa8f30f"}, + {file = "Pillow-9.4.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:d7081c084ceb58278dd3cf81f836bc818978c0ccc770cbbb202125ddabec6628"}, + {file = "Pillow-9.4.0-cp38-cp38-win32.whl", hash = "sha256:df41112ccce5d47770a0c13651479fbcd8793f34232a2dd9faeccb75eb5d0d0d"}, + {file = "Pillow-9.4.0-cp38-cp38-win_amd64.whl", hash = "sha256:7a21222644ab69ddd9967cfe6f2bb420b460dae4289c9d40ff9a4896e7c35c9a"}, + {file = "Pillow-9.4.0-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:0f3269304c1a7ce82f1759c12ce731ef9b6e95b6df829dccd9fe42912cc48569"}, + {file = "Pillow-9.4.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:cb362e3b0976dc994857391b776ddaa8c13c28a16f80ac6522c23d5257156bed"}, + {file = "Pillow-9.4.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a2e0f87144fcbbe54297cae708c5e7f9da21a4646523456b00cc956bd4c65815"}, + {file = "Pillow-9.4.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:28676836c7796805914b76b1837a40f76827ee0d5398f72f7dcc634bae7c6264"}, + {file = "Pillow-9.4.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0884ba7b515163a1a05440a138adeb722b8a6ae2c2b33aea93ea3118dd3a899e"}, + {file = "Pillow-9.4.0-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:53dcb50fbdc3fb2c55431a9b30caeb2f7027fcd2aeb501459464f0214200a503"}, + {file = "Pillow-9.4.0-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:e8c5cf126889a4de385c02a2c3d3aba4b00f70234bfddae82a5eaa3ee6d5e3e6"}, + {file = "Pillow-9.4.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:6c6b1389ed66cdd174d040105123a5a1bc91d0aa7059c7261d20e583b6d8cbd2"}, + {file = "Pillow-9.4.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:0dd4c681b82214b36273c18ca7ee87065a50e013112eea7d78c7a1b89a739153"}, + {file = "Pillow-9.4.0-cp39-cp39-win32.whl", hash = "sha256:6d9dfb9959a3b0039ee06c1a1a90dc23bac3b430842dcb97908ddde05870601c"}, + {file = "Pillow-9.4.0-cp39-cp39-win_amd64.whl", hash = "sha256:54614444887e0d3043557d9dbc697dbb16cfb5a35d672b7a0fcc1ed0cf1c600b"}, + {file = "Pillow-9.4.0-pp38-pypy38_pp73-macosx_10_10_x86_64.whl", hash = "sha256:b9b752ab91e78234941e44abdecc07f1f0d8f51fb62941d32995b8161f68cfe5"}, + {file = "Pillow-9.4.0-pp38-pypy38_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d3b56206244dc8711f7e8b7d6cad4663917cd5b2d950799425076681e8766286"}, + {file = "Pillow-9.4.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aabdab8ec1e7ca7f1434d042bf8b1e92056245fb179790dc97ed040361f16bfd"}, + {file = "Pillow-9.4.0-pp38-pypy38_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:db74f5562c09953b2c5f8ec4b7dfd3f5421f31811e97d1dbc0a7c93d6e3a24df"}, + {file = "Pillow-9.4.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:e9d7747847c53a16a729b6ee5e737cf170f7a16611c143d95aa60a109a59c336"}, + {file = "Pillow-9.4.0-pp39-pypy39_pp73-macosx_10_10_x86_64.whl", hash = "sha256:b52ff4f4e002f828ea6483faf4c4e8deea8d743cf801b74910243c58acc6eda3"}, + {file = "Pillow-9.4.0-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:575d8912dca808edd9acd6f7795199332696d3469665ef26163cd090fa1f8bfa"}, + {file = "Pillow-9.4.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c3c4ed2ff6760e98d262e0cc9c9a7f7b8a9f61aa4d47c58835cdaf7b0b8811bb"}, + {file = "Pillow-9.4.0-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:e621b0246192d3b9cb1dc62c78cfa4c6f6d2ddc0ec207d43c0dedecb914f152a"}, + {file = "Pillow-9.4.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:8f127e7b028900421cad64f51f75c051b628db17fb00e099eb148761eed598c9"}, + {file = "Pillow-9.4.0.tar.gz", hash = "sha256:a1c2d7780448eb93fbcc3789bf3916aa5720d942e37945f4056680317f1cd23e"}, +] + +[package.extras] +docs = ["furo", "olefile", "sphinx (>=2.4)", "sphinx-copybutton", "sphinx-inline-tabs", "sphinx-issues (>=3.0.1)", "sphinx-removed-in", "sphinxext-opengraph"] +tests = ["check-manifest", "coverage", "defusedxml", "markdown2", "olefile", "packaging", "pyroma", "pytest", "pytest-cov", "pytest-timeout"] + +[[package]] +name = "pip" +version = "23.0.1" +description = "The PyPA recommended tool for installing Python packages." +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "pip-23.0.1-py3-none-any.whl", hash = "sha256:236bcb61156d76c4b8a05821b988c7b8c35bf0da28a4b614e8d6ab5212c25c6f"}, + {file = "pip-23.0.1.tar.gz", hash = "sha256:cd015ea1bfb0fcef59d8a286c1f8bebcb983f6317719d415dc5351efb7cd7024"}, +] + +[[package]] +name = "platformdirs" +version = "3.1.1" +description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "platformdirs-3.1.1-py3-none-any.whl", hash = "sha256:e5986afb596e4bb5bde29a79ac9061aa955b94fca2399b7aaac4090860920dd8"}, + {file = "platformdirs-3.1.1.tar.gz", hash = "sha256:024996549ee88ec1a9aa99ff7f8fc819bb59e2c3477b410d90a16d32d6e707aa"}, +] + +[package.extras] +docs = ["furo (>=2022.12.7)", "proselint (>=0.13)", "sphinx (>=6.1.3)", "sphinx-autodoc-typehints (>=1.22,!=1.23.4)"] +test = ["appdirs (==1.4.4)", "covdefaults (>=2.2.2)", "pytest (>=7.2.1)", "pytest-cov (>=4)", "pytest-mock (>=3.10)"] + +[[package]] +name = "pluggy" +version = "1.0.0" +description = "plugin and hook calling mechanisms for python" +category = "dev" +optional = false +python-versions = ">=3.6" +files = [ + {file = "pluggy-1.0.0-py2.py3-none-any.whl", hash = "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"}, + {file = "pluggy-1.0.0.tar.gz", hash = "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159"}, +] + +[package.extras] +dev = ["pre-commit", "tox"] +testing = ["pytest", "pytest-benchmark"] + +[[package]] +name = "pockets" +version = "0.9.1" +description = "A collection of helpful Python tools!" +category = "dev" +optional = false +python-versions = "*" +files = [ + {file = "pockets-0.9.1-py2.py3-none-any.whl", hash = "sha256:68597934193c08a08eb2bf6a1d85593f627c22f9b065cc727a4f03f669d96d86"}, + {file = "pockets-0.9.1.tar.gz", hash = "sha256:9320f1a3c6f7a9133fe3b571f283bcf3353cd70249025ae8d618e40e9f7e92b3"}, +] + +[package.dependencies] +six = ">=1.5.2" + +[[package]] +name = "protobuf" +version = "3.19.6" +description = "Protocol Buffers" +category = "main" +optional = false +python-versions = ">=3.5" +files = [ + {file = "protobuf-3.19.6-cp310-cp310-manylinux2014_aarch64.whl", hash = "sha256:010be24d5a44be7b0613750ab40bc8b8cedc796db468eae6c779b395f50d1fa1"}, + {file = "protobuf-3.19.6-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:11478547958c2dfea921920617eb457bc26867b0d1aa065ab05f35080c5d9eb6"}, + {file = "protobuf-3.19.6-cp310-cp310-win32.whl", hash = "sha256:559670e006e3173308c9254d63facb2c03865818f22204037ab76f7a0ff70b5f"}, + {file = "protobuf-3.19.6-cp310-cp310-win_amd64.whl", hash = "sha256:347b393d4dd06fb93a77620781e11c058b3b0a5289262f094379ada2920a3730"}, + {file = "protobuf-3.19.6-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:a8ce5ae0de28b51dff886fb922012dad885e66176663950cb2344c0439ecb473"}, + {file = "protobuf-3.19.6-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90b0d02163c4e67279ddb6dc25e063db0130fc299aefabb5d481053509fae5c8"}, + {file = "protobuf-3.19.6-cp36-cp36m-win32.whl", hash = "sha256:30f5370d50295b246eaa0296533403961f7e64b03ea12265d6dfce3a391d8992"}, + {file = "protobuf-3.19.6-cp36-cp36m-win_amd64.whl", hash = "sha256:0c0714b025ec057b5a7600cb66ce7c693815f897cfda6d6efb58201c472e3437"}, + {file = "protobuf-3.19.6-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:5057c64052a1f1dd7d4450e9aac25af6bf36cfbfb3a1cd89d16393a036c49157"}, + {file = "protobuf-3.19.6-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:bb6776bd18f01ffe9920e78e03a8676530a5d6c5911934c6a1ac6eb78973ecb6"}, + {file = "protobuf-3.19.6-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:84a04134866861b11556a82dd91ea6daf1f4925746b992f277b84013a7cc1229"}, + {file = "protobuf-3.19.6-cp37-cp37m-win32.whl", hash = "sha256:4bc98de3cdccfb5cd769620d5785b92c662b6bfad03a202b83799b6ed3fa1fa7"}, + {file = "protobuf-3.19.6-cp37-cp37m-win_amd64.whl", hash = "sha256:aa3b82ca1f24ab5326dcf4ea00fcbda703e986b22f3d27541654f749564d778b"}, + {file = "protobuf-3.19.6-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:2b2d2913bcda0e0ec9a784d194bc490f5dc3d9d71d322d070b11a0ade32ff6ba"}, + {file = "protobuf-3.19.6-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:d0b635cefebd7a8a0f92020562dead912f81f401af7e71f16bf9506ff3bdbb38"}, + {file = "protobuf-3.19.6-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7a552af4dc34793803f4e735aabe97ffc45962dfd3a237bdde242bff5a3de684"}, + {file = "protobuf-3.19.6-cp38-cp38-win32.whl", hash = "sha256:0469bc66160180165e4e29de7f445e57a34ab68f49357392c5b2f54c656ab25e"}, + {file = "protobuf-3.19.6-cp38-cp38-win_amd64.whl", hash = "sha256:91d5f1e139ff92c37e0ff07f391101df77e55ebb97f46bbc1535298d72019462"}, + {file = "protobuf-3.19.6-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c0ccd3f940fe7f3b35a261b1dd1b4fc850c8fde9f74207015431f174be5976b3"}, + {file = "protobuf-3.19.6-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:30a15015d86b9c3b8d6bf78d5b8c7749f2512c29f168ca259c9d7727604d0e39"}, + {file = "protobuf-3.19.6-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:878b4cd080a21ddda6ac6d1e163403ec6eea2e206cf225982ae04567d39be7b0"}, + {file = "protobuf-3.19.6-cp39-cp39-win32.whl", hash = "sha256:5a0d7539a1b1fb7e76bf5faa0b44b30f812758e989e59c40f77a7dab320e79b9"}, + {file = "protobuf-3.19.6-cp39-cp39-win_amd64.whl", hash = "sha256:bbf5cea5048272e1c60d235c7bd12ce1b14b8a16e76917f371c718bd3005f045"}, + {file = "protobuf-3.19.6-py2.py3-none-any.whl", hash = "sha256:14082457dc02be946f60b15aad35e9f5c69e738f80ebbc0900a19bc83734a5a4"}, + {file = "protobuf-3.19.6.tar.gz", hash = "sha256:5f5540d57a43042389e87661c6eaa50f47c19c6176e8cf1c4f287aeefeccb5c4"}, +] + +[[package]] +name = "pyasn1" +version = "0.4.8" +description = "ASN.1 types and codecs" +category = "main" +optional = false +python-versions = "*" +files = [ + {file = "pyasn1-0.4.8-py2.py3-none-any.whl", hash = "sha256:39c7e2ec30515947ff4e87fb6f456dfc6e84857d34be479c9d4a4ba4bf46aa5d"}, + {file = "pyasn1-0.4.8.tar.gz", hash = "sha256:aef77c9fb94a3ac588e87841208bdec464471d9871bd5050a287cc9a475cd0ba"}, +] + +[[package]] +name = "pyasn1-modules" +version = "0.2.8" +description = "A collection of ASN.1-based protocols modules." +category = "main" +optional = false +python-versions = "*" +files = [ + {file = "pyasn1-modules-0.2.8.tar.gz", hash = "sha256:905f84c712230b2c592c19470d3ca8d552de726050d1d1716282a1f6146be65e"}, + {file = "pyasn1_modules-0.2.8-py2.py3-none-any.whl", hash = "sha256:a50b808ffeb97cb3601dd25981f6b016cbb3d31fbf57a8b8a87428e6158d0c74"}, +] + +[package.dependencies] +pyasn1 = ">=0.4.6,<0.5.0" + +[[package]] +name = "pycodestyle" +version = "2.7.0" +description = "Python style guide checker" +category = "dev" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +files = [ + {file = "pycodestyle-2.7.0-py2.py3-none-any.whl", hash = "sha256:514f76d918fcc0b55c6680472f0a37970994e07bbb80725808c17089be302068"}, + {file = "pycodestyle-2.7.0.tar.gz", hash = "sha256:c389c1d06bf7904078ca03399a4816f974a1d590090fecea0c63ec26ebaf1cef"}, +] + +[[package]] +name = "pyeda" +version = "0.28.0" +description = "Python Electronic Design Automation" +category = "main" +optional = false +python-versions = "*" +files = [ {file = "pyeda-0.28.0.tar.gz", hash = "sha256:07185f458d5d0b2ba5058da8b95dad6ab7684ceaf41237a25bcd3f005490f59d"}, {file = "pyeda-0.28.0.zip", hash = "sha256:e34c7a6332d24914d1be524b3d9fe97feb165dbfe63473b3a112c1aeda2c002e"}, ] -pyflakes = [ - {file = "pyflakes-2.2.0-py2.py3-none-any.whl", hash = "sha256:0d94e0e05a19e57a99444b6ddcf9a6eb2e5c68d3ca1e98e90707af8152c90a92"}, - {file = "pyflakes-2.2.0.tar.gz", hash = "sha256:35b2d75ee967ea93b55750aa9edbbf72813e06a66ba54438df2cfac9e3c27fc8"}, -] -pygraphviz = [ - {file = "pygraphviz-1.6.zip", hash = "sha256:411ae84a5bc313e3e1523a1cace59159f512336318a510573b47f824edef8860"}, -] -pymint = [ - {file = "pymint-0.2.11-py3-none-any.whl", hash = "sha256:63b0b0ffda571fd434f29a11e8c7afa711fdc4fe92177262bfc79bd5d113fa42"}, - {file = "pymint-0.2.11.tar.gz", hash = "sha256:7301b75556b24b47622419e23619ce0b5a3f71bfd8e9f24807c91d5470043d0e"}, -] -pyrsistent = [ - {file = "pyrsistent-0.18.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:f4c8cabb46ff8e5d61f56a037974228e978f26bfefce4f61a4b1ac0ba7a2ab72"}, - {file = "pyrsistent-0.18.0-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:da6e5e818d18459fa46fac0a4a4e543507fe1110e808101277c5a2b5bab0cd2d"}, - {file = "pyrsistent-0.18.0-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:5e4395bbf841693eaebaa5bb5c8f5cdbb1d139e07c975c682ec4e4f8126e03d2"}, - {file = "pyrsistent-0.18.0-cp36-cp36m-win32.whl", hash = "sha256:527be2bfa8dc80f6f8ddd65242ba476a6c4fb4e3aedbf281dfbac1b1ed4165b1"}, - {file = "pyrsistent-0.18.0-cp36-cp36m-win_amd64.whl", hash = "sha256:2aaf19dc8ce517a8653746d98e962ef480ff34b6bc563fc067be6401ffb457c7"}, - {file = "pyrsistent-0.18.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:58a70d93fb79dc585b21f9d72487b929a6fe58da0754fa4cb9f279bb92369396"}, - {file = "pyrsistent-0.18.0-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:4916c10896721e472ee12c95cdc2891ce5890898d2f9907b1b4ae0f53588b710"}, - {file = "pyrsistent-0.18.0-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:73ff61b1411e3fb0ba144b8f08d6749749775fe89688093e1efef9839d2dcc35"}, - {file = "pyrsistent-0.18.0-cp37-cp37m-win32.whl", hash = "sha256:b29b869cf58412ca5738d23691e96d8aff535e17390128a1a52717c9a109da4f"}, - {file = "pyrsistent-0.18.0-cp37-cp37m-win_amd64.whl", hash = "sha256:097b96f129dd36a8c9e33594e7ebb151b1515eb52cceb08474c10a5479e799f2"}, - {file = "pyrsistent-0.18.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:772e94c2c6864f2cd2ffbe58bb3bdefbe2a32afa0acb1a77e472aac831f83427"}, - {file = "pyrsistent-0.18.0-cp38-cp38-manylinux1_i686.whl", hash = "sha256:c1a9ff320fa699337e05edcaae79ef8c2880b52720bc031b219e5b5008ebbdef"}, - {file = "pyrsistent-0.18.0-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:cd3caef37a415fd0dae6148a1b6957a8c5f275a62cca02e18474608cb263640c"}, - {file = "pyrsistent-0.18.0-cp38-cp38-win32.whl", hash = "sha256:e79d94ca58fcafef6395f6352383fa1a76922268fa02caa2272fff501c2fdc78"}, - {file = "pyrsistent-0.18.0-cp38-cp38-win_amd64.whl", hash = "sha256:a0c772d791c38bbc77be659af29bb14c38ced151433592e326361610250c605b"}, - {file = "pyrsistent-0.18.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:d5ec194c9c573aafaceebf05fc400656722793dac57f254cd4741f3c27ae57b4"}, - {file = "pyrsistent-0.18.0-cp39-cp39-manylinux1_i686.whl", hash = "sha256:6b5eed00e597b5b5773b4ca30bd48a5774ef1e96f2a45d105db5b4ebb4bca680"}, - {file = "pyrsistent-0.18.0-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:48578680353f41dca1ca3dc48629fb77dfc745128b56fc01096b2530c13fd426"}, - {file = "pyrsistent-0.18.0-cp39-cp39-win32.whl", hash = "sha256:f3ef98d7b76da5eb19c37fda834d50262ff9167c65658d1d8f974d2e4d90676b"}, - {file = "pyrsistent-0.18.0-cp39-cp39-win_amd64.whl", hash = "sha256:404e1f1d254d314d55adb8d87f4f465c8693d6f902f67eb6ef5b4526dc58e6ea"}, - {file = "pyrsistent-0.18.0.tar.gz", hash = "sha256:773c781216f8c2900b42a7b638d5b517bb134ae1acbebe4d1e8f1f41ea60eb4b"}, -] -regex = [ - {file = "regex-2020.11.11-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:dd7bee615680d940dd44ac0a479f2bc5f73d6ca63a5915cd8d30739c14ca522c"}, - {file = "regex-2020.11.11-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:3002ee2d4e8bbe4656237627203d8290a562d1fc1962deee470905ab63570345"}, - {file = "regex-2020.11.11-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:064d2fc83ab4ee0055fcc1ef38ec60e505742850a40061f854ac64cb3d8d6dd3"}, - {file = "regex-2020.11.11-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:83a390a653c13be1ab26287240df1fd9324ca8a0d31b603fa57cd7d9520648fa"}, - {file = "regex-2020.11.11-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:412969d58ecd4f576510ec88bcb7602e9e582bbef78859ed8c9ca4de4f9e891c"}, - {file = "regex-2020.11.11-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:ccfea4911ac28a8f744096bce1559e0bd86b09a53c8a9d5856ca8e1f5f4de1f5"}, - {file = "regex-2020.11.11-cp36-cp36m-manylinux2014_i686.whl", hash = "sha256:cefcdb2ac3b67fd9f7244820ce1965c8cf352366199cc1358d67c6cc3c5c8bbc"}, - {file = "regex-2020.11.11-cp36-cp36m-manylinux2014_x86_64.whl", hash = "sha256:9e8b3187f6beea8e56cb4b33c35049cbe376cf69aefaee5bc035309d88c98ca5"}, - {file = "regex-2020.11.11-cp36-cp36m-win32.whl", hash = "sha256:787e44e5f4fd027dd90b5ee0240b05dc1752cb43c2903617f25baa495fe551e9"}, - {file = "regex-2020.11.11-cp36-cp36m-win_amd64.whl", hash = "sha256:a9f76d9122359b09e38f27cd9c41729169171cf0fd73ec5b22cc4628f9e486ca"}, - {file = "regex-2020.11.11-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:6d128368def4b0cd95c0fc9d99a89ae73c083b25e67f27a410830e30f9df0edc"}, - {file = "regex-2020.11.11-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:df50ba964812606663ca9d23d374036bc5ae3d71e86168409cdd84ca7948d8a3"}, - {file = "regex-2020.11.11-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:d1e57c16c4840f1c3543507742e99b8398609474a0e6a6925476914479de3488"}, - {file = "regex-2020.11.11-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:6e50b3b417ab2fd67bfa6235f0df4782fe2ff8be83f0c4435e1dc43d25052ee8"}, - {file = "regex-2020.11.11-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:bb17a7fe9c47167337009ce18cd6e6b3edf3ca0063bf6bed6ce02515129c016a"}, - {file = "regex-2020.11.11-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:826d0119f14f9a9ce25999a13ed5922c785b50e469800f6e5a6721318650ef49"}, - {file = "regex-2020.11.11-cp37-cp37m-manylinux2014_i686.whl", hash = "sha256:8cc3717146ce4040419639cf45455663a002a554806ddac46304acc5bd41dae2"}, - {file = "regex-2020.11.11-cp37-cp37m-manylinux2014_x86_64.whl", hash = "sha256:86ad88c7c2512094a85b0a01ce053bab1e28eafb8f3868bb8c22f4903e33f147"}, - {file = "regex-2020.11.11-cp37-cp37m-win32.whl", hash = "sha256:e03867f3baf64ecab47dfc9ddb58afc67acb6a0f80f6cf8ff9fa82962ec4d1cd"}, - {file = "regex-2020.11.11-cp37-cp37m-win_amd64.whl", hash = "sha256:56d1e298bb6482d0466399a6383181bf2627c37ad414e205b3ce0f85aa140be7"}, - {file = "regex-2020.11.11-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:19ac2bf0048a2f4d460ee20647e84ca160512a7ee8af844dc9207720778470f1"}, - {file = "regex-2020.11.11-cp38-cp38-manylinux1_i686.whl", hash = "sha256:84ab584dcb5e81815040d86148805a808acb0bee303d19638fe2f9488d704bc1"}, - {file = "regex-2020.11.11-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:4159ecf20dffea07f4a7241b2a236f90eb622c7e8caab9f43caba5f27ca37284"}, - {file = "regex-2020.11.11-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:8060be04baec546fe3afa6975d2998e15d1b655d7255f0e6b0ed3f482cccc218"}, - {file = "regex-2020.11.11-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:cdb98be55db1b94c950822cbc10d3d768f01e184365851ebb42cd377486ced7b"}, - {file = "regex-2020.11.11-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:11d9100bd874ce8b2a037db9150e732cd768359fc25fe5f77973208aa24eb13e"}, - {file = "regex-2020.11.11-cp38-cp38-manylinux2014_i686.whl", hash = "sha256:0951c78fa4cb26d1278a4b3784fcf973fc97ec39c07483328a74b034b0cc569c"}, - {file = "regex-2020.11.11-cp38-cp38-manylinux2014_x86_64.whl", hash = "sha256:c8b1ad791debd67221fb1266f8d09730ae927acacb32d0dad9fd07a7d341a28f"}, - {file = "regex-2020.11.11-cp38-cp38-win32.whl", hash = "sha256:beae9db1545f8116cfc9301a9601e9c975bb56ca22a38ac0fe06a72c3460f31a"}, - {file = "regex-2020.11.11-cp38-cp38-win_amd64.whl", hash = "sha256:48e94218f06317b6d32feb4ecff8b6025695450009bcb3291fb23daf79689431"}, - {file = "regex-2020.11.11-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c67fd5f3ad81f8301184354014e8e7510ab77e0c7e450a427d77f28ae8effbef"}, - {file = "regex-2020.11.11-cp39-cp39-manylinux1_i686.whl", hash = "sha256:e7cdd5ee8053c82607432b7ebad37e2ece54548fef2b254f7bce6f7831904586"}, - {file = "regex-2020.11.11-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:394b5be4fa72354a78763b317f82997ad881896dd4a860e429a6fa74afaacb07"}, - {file = "regex-2020.11.11-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:3b46a4c73ec1f25361147a7a0fd86084f3627dc78d09bcbe14e70db12683efec"}, - {file = "regex-2020.11.11-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:267d1b13f863e664150948ce2a9ed4927bf4ac7a068780f1ee8af83352aa17a2"}, - {file = "regex-2020.11.11-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:68267a7a5fb0bd9676b86f967143b6a6ecefb3eed4042ecc9e7f0e014aef8f74"}, - {file = "regex-2020.11.11-cp39-cp39-manylinux2014_i686.whl", hash = "sha256:e899b69dd5d26655cb454835ea2fceb18832c9ee9c4fb45dc4cf8a6089d35312"}, - {file = "regex-2020.11.11-cp39-cp39-manylinux2014_x86_64.whl", hash = "sha256:396411bb5a7849aeda9c49873b8295919fdc118c50b57122b09cb2097047c118"}, - {file = "regex-2020.11.11-cp39-cp39-win32.whl", hash = "sha256:32f8714c4bcc4b0d2aa259b1647e3c5b6cfe2e923c6c124234a5e03408224227"}, - {file = "regex-2020.11.11-cp39-cp39-win_amd64.whl", hash = "sha256:bf02ab95ff5261ba108725dbd795bf6395eaac1b8468b41472d82d35b12b0295"}, - {file = "regex-2020.11.11.tar.gz", hash = "sha256:0a235841237d4487329bcabcb5b902858f7967f5e684e08e968367f25b2c3d37"}, -] -rope = [ - {file = "rope-0.18.0.tar.gz", hash = "sha256:786b5c38c530d4846aa68a42604f61b4e69a493390e3ca11b88df0fbfdc3ed04"}, + +[[package]] +name = "pyfiglet" +version = "0.8.post1" +description = "Pure-python FIGlet implementation" +category = "main" +optional = false +python-versions = "*" +files = [ + {file = "pyfiglet-0.8.post1-py2.py3-none-any.whl", hash = "sha256:d555bcea17fbeaf70eaefa48bb119352487e629c9b56f30f383e2c62dd67a01c"}, + {file = "pyfiglet-0.8.post1.tar.gz", hash = "sha256:c6c2321755d09267b438ec7b936825a4910fec696292139e664ca8670e103639"}, ] -six = [ - {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, - {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, + +[[package]] +name = "pyflakes" +version = "2.3.1" +description = "passive checker of Python programs" +category = "dev" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +files = [ + {file = "pyflakes-2.3.1-py2.py3-none-any.whl", hash = "sha256:7893783d01b8a89811dd72d7dfd4d84ff098e5eed95cfa8905b22bbffe52efc3"}, + {file = "pyflakes-2.3.1.tar.gz", hash = "sha256:f5bc8ecabc05bb9d291eb5203d6810b49040f6ff446a756326104746cc00c1db"}, +] + +[[package]] +name = "pygments" +version = "2.14.0" +description = "Pygments is a syntax highlighting package written in Python." +category = "dev" +optional = false +python-versions = ">=3.6" +files = [ + {file = "Pygments-2.14.0-py3-none-any.whl", hash = "sha256:fa7bd7bd2771287c0de303af8bfdfc731f51bd2c6a47ab69d117138893b82717"}, + {file = "Pygments-2.14.0.tar.gz", hash = "sha256:b3ed06a9e8ac9a9aae5a6f5dbe78a8a58655d17b43b93c078f094ddc476ae297"}, +] + +[package.extras] +plugins = ["importlib-metadata"] + +[[package]] +name = "pygraphviz" +version = "1.10" +description = "Python interface to Graphviz" +category = "main" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pygraphviz-1.10.zip", hash = "sha256:457e093a888128903251a266a8cc16b4ba93f3f6334b3ebfed92c7471a74d867"}, +] + +[[package]] +name = "pymint" +version = "0.3.1" +description = "MINT is a human readable language to describe Microfluidic Hardware Netlists. MINT is the name of the Microfluidic Netlist language used to describe microfluidic devices for Fluigi to place and route. Mint is a flavor of (MHDL) Microfluidic Hardware Description Language that can be used to represent Microfluidic Circuits." +category = "main" +optional = false +python-versions = ">=3.8,<3.11" +files = [] +develop = true + +[package.dependencies] +antlr4-python3-runtime = "^4.8" +click = "^8.1.3" +install = "^1.3.3" +networkx = "^3.0" +parchmint = {path = "../pyparchmint", develop = true} +pyfiglet = "^0.8.post1" +pygraphviz = "^1.9" + +[package.extras] +docs = [] + +[package.source] +type = "directory" +url = "pymint" + +[[package]] +name = "pyparsing" +version = "3.0.9" +description = "pyparsing module - Classes and methods to define and execute parsing grammars" +category = "main" +optional = false +python-versions = ">=3.6.8" +files = [ + {file = "pyparsing-3.0.9-py3-none-any.whl", hash = "sha256:5026bae9a10eeaefb61dab2f09052b9f4307d44aee4eda64b309723d8d206bbc"}, + {file = "pyparsing-3.0.9.tar.gz", hash = "sha256:2b020ecf7d21b687f219b71ecad3631f644a47f01403fa1d1036b0c6416d70fb"}, +] + +[package.extras] +diagrams = ["jinja2", "railroad-diagrams"] + +[[package]] +name = "pyrsistent" +version = "0.19.3" +description = "Persistent/Functional/Immutable data structures" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "pyrsistent-0.19.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:20460ac0ea439a3e79caa1dbd560344b64ed75e85d8703943e0b66c2a6150e4a"}, + {file = "pyrsistent-0.19.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4c18264cb84b5e68e7085a43723f9e4c1fd1d935ab240ce02c0324a8e01ccb64"}, + {file = "pyrsistent-0.19.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4b774f9288dda8d425adb6544e5903f1fb6c273ab3128a355c6b972b7df39dcf"}, + {file = "pyrsistent-0.19.3-cp310-cp310-win32.whl", hash = "sha256:5a474fb80f5e0d6c9394d8db0fc19e90fa540b82ee52dba7d246a7791712f74a"}, + {file = "pyrsistent-0.19.3-cp310-cp310-win_amd64.whl", hash = "sha256:49c32f216c17148695ca0e02a5c521e28a4ee6c5089f97e34fe24163113722da"}, + {file = "pyrsistent-0.19.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:f0774bf48631f3a20471dd7c5989657b639fd2d285b861237ea9e82c36a415a9"}, + {file = "pyrsistent-0.19.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3ab2204234c0ecd8b9368dbd6a53e83c3d4f3cab10ecaf6d0e772f456c442393"}, + {file = "pyrsistent-0.19.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e42296a09e83028b3476f7073fcb69ffebac0e66dbbfd1bd847d61f74db30f19"}, + {file = "pyrsistent-0.19.3-cp311-cp311-win32.whl", hash = "sha256:64220c429e42a7150f4bfd280f6f4bb2850f95956bde93c6fda1b70507af6ef3"}, + {file = "pyrsistent-0.19.3-cp311-cp311-win_amd64.whl", hash = "sha256:016ad1afadf318eb7911baa24b049909f7f3bb2c5b1ed7b6a8f21db21ea3faa8"}, + {file = "pyrsistent-0.19.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c4db1bd596fefd66b296a3d5d943c94f4fac5bcd13e99bffe2ba6a759d959a28"}, + {file = "pyrsistent-0.19.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aeda827381f5e5d65cced3024126529ddc4289d944f75e090572c77ceb19adbf"}, + {file = "pyrsistent-0.19.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:42ac0b2f44607eb92ae88609eda931a4f0dfa03038c44c772e07f43e738bcac9"}, + {file = "pyrsistent-0.19.3-cp37-cp37m-win32.whl", hash = "sha256:e8f2b814a3dc6225964fa03d8582c6e0b6650d68a232df41e3cc1b66a5d2f8d1"}, + {file = "pyrsistent-0.19.3-cp37-cp37m-win_amd64.whl", hash = "sha256:c9bb60a40a0ab9aba40a59f68214eed5a29c6274c83b2cc206a359c4a89fa41b"}, + {file = "pyrsistent-0.19.3-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:a2471f3f8693101975b1ff85ffd19bb7ca7dd7c38f8a81701f67d6b4f97b87d8"}, + {file = "pyrsistent-0.19.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cc5d149f31706762c1f8bda2e8c4f8fead6e80312e3692619a75301d3dbb819a"}, + {file = "pyrsistent-0.19.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3311cb4237a341aa52ab8448c27e3a9931e2ee09561ad150ba94e4cfd3fc888c"}, + {file = "pyrsistent-0.19.3-cp38-cp38-win32.whl", hash = "sha256:f0e7c4b2f77593871e918be000b96c8107da48444d57005b6a6bc61fb4331b2c"}, + {file = "pyrsistent-0.19.3-cp38-cp38-win_amd64.whl", hash = "sha256:c147257a92374fde8498491f53ffa8f4822cd70c0d85037e09028e478cababb7"}, + {file = "pyrsistent-0.19.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:b735e538f74ec31378f5a1e3886a26d2ca6351106b4dfde376a26fc32a044edc"}, + {file = "pyrsistent-0.19.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:99abb85579e2165bd8522f0c0138864da97847875ecbd45f3e7e2af569bfc6f2"}, + {file = "pyrsistent-0.19.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3a8cb235fa6d3fd7aae6a4f1429bbb1fec1577d978098da1252f0489937786f3"}, + {file = "pyrsistent-0.19.3-cp39-cp39-win32.whl", hash = "sha256:c74bed51f9b41c48366a286395c67f4e894374306b197e62810e0fdaf2364da2"}, + {file = "pyrsistent-0.19.3-cp39-cp39-win_amd64.whl", hash = "sha256:878433581fc23e906d947a6814336eee031a00e6defba224234169ae3d3d6a98"}, + {file = "pyrsistent-0.19.3-py3-none-any.whl", hash = "sha256:ccf0d6bd208f8111179f0c26fdf84ed7c3891982f2edaeae7422575f47e66b64"}, + {file = "pyrsistent-0.19.3.tar.gz", hash = "sha256:1a2994773706bbb4995c31a97bc94f1418314923bd1048c6d964837040376440"}, ] -tabulate = [ - {file = "tabulate-0.8.9-py3-none-any.whl", hash = "sha256:d7c013fe7abbc5e491394e10fa845f8f32fe54f8dc60c6622c6cf482d25d47e4"}, - {file = "tabulate-0.8.9.tar.gz", hash = "sha256:eb1d13f25760052e8931f2ef80aaf6045a6cceb47514db8beab24cded16f13a7"}, -] -toml = [ - {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"}, - {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"}, -] -typed-ast = [ - {file = "typed_ast-1.4.1-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:73d785a950fc82dd2a25897d525d003f6378d1cb23ab305578394694202a58c3"}, - {file = "typed_ast-1.4.1-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:aaee9905aee35ba5905cfb3c62f3e83b3bec7b39413f0a7f19be4e547ea01ebb"}, - {file = "typed_ast-1.4.1-cp35-cp35m-win32.whl", hash = "sha256:0c2c07682d61a629b68433afb159376e24e5b2fd4641d35424e462169c0a7919"}, - {file = "typed_ast-1.4.1-cp35-cp35m-win_amd64.whl", hash = "sha256:4083861b0aa07990b619bd7ddc365eb7fa4b817e99cf5f8d9cf21a42780f6e01"}, - {file = "typed_ast-1.4.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:269151951236b0f9a6f04015a9004084a5ab0d5f19b57de779f908621e7d8b75"}, - {file = "typed_ast-1.4.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:24995c843eb0ad11a4527b026b4dde3da70e1f2d8806c99b7b4a7cf491612652"}, - {file = "typed_ast-1.4.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:fe460b922ec15dd205595c9b5b99e2f056fd98ae8f9f56b888e7a17dc2b757e7"}, - {file = "typed_ast-1.4.1-cp36-cp36m-win32.whl", hash = "sha256:4e3e5da80ccbebfff202a67bf900d081906c358ccc3d5e3c8aea42fdfdfd51c1"}, - {file = "typed_ast-1.4.1-cp36-cp36m-win_amd64.whl", hash = "sha256:249862707802d40f7f29f6e1aad8d84b5aa9e44552d2cc17384b209f091276aa"}, - {file = "typed_ast-1.4.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:8ce678dbaf790dbdb3eba24056d5364fb45944f33553dd5869b7580cdbb83614"}, - {file = "typed_ast-1.4.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:c9e348e02e4d2b4a8b2eedb48210430658df6951fa484e59de33ff773fbd4b41"}, - {file = "typed_ast-1.4.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:bcd3b13b56ea479b3650b82cabd6b5343a625b0ced5429e4ccad28a8973f301b"}, - {file = "typed_ast-1.4.1-cp37-cp37m-win32.whl", hash = "sha256:d5d33e9e7af3b34a40dc05f498939f0ebf187f07c385fd58d591c533ad8562fe"}, - {file = "typed_ast-1.4.1-cp37-cp37m-win_amd64.whl", hash = "sha256:0666aa36131496aed8f7be0410ff974562ab7eeac11ef351def9ea6fa28f6355"}, - {file = "typed_ast-1.4.1-cp38-cp38-macosx_10_15_x86_64.whl", hash = "sha256:d205b1b46085271b4e15f670058ce182bd1199e56b317bf2ec004b6a44f911f6"}, - {file = "typed_ast-1.4.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:6daac9731f172c2a22ade6ed0c00197ee7cc1221aa84cfdf9c31defeb059a907"}, - {file = "typed_ast-1.4.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:498b0f36cc7054c1fead3d7fc59d2150f4d5c6c56ba7fb150c013fbc683a8d2d"}, - {file = "typed_ast-1.4.1-cp38-cp38-win32.whl", hash = "sha256:715ff2f2df46121071622063fc7543d9b1fd19ebfc4f5c8895af64a77a8c852c"}, - {file = "typed_ast-1.4.1-cp38-cp38-win_amd64.whl", hash = "sha256:fc0fea399acb12edbf8a628ba8d2312f583bdbdb3335635db062fa98cf71fca4"}, - {file = "typed_ast-1.4.1-cp39-cp39-macosx_10_15_x86_64.whl", hash = "sha256:d43943ef777f9a1c42bf4e552ba23ac77a6351de620aa9acf64ad54933ad4d34"}, - {file = "typed_ast-1.4.1.tar.gz", hash = "sha256:8c8aaad94455178e3187ab22c8b01a3837f8ee50e09cf31f1ba129eb293ec30b"}, -] -typing-extensions = [ - {file = "typing_extensions-3.7.4.3-py2-none-any.whl", hash = "sha256:dafc7639cde7f1b6e1acc0f457842a83e722ccca8eef5270af2d74792619a89f"}, - {file = "typing_extensions-3.7.4.3-py3-none-any.whl", hash = "sha256:7cb407020f00f7bfc3cb3e7881628838e69d8f3fcab2f64742a5e76b2f841918"}, - {file = "typing_extensions-3.7.4.3.tar.gz", hash = "sha256:99d4073b617d30288f569d3f13d2bd7548c3a7e4c8de87db09a9d29bb3a4a60c"}, + +[[package]] +name = "pytest" +version = "7.2.2" +description = "pytest: simple powerful testing with Python" +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "pytest-7.2.2-py3-none-any.whl", hash = "sha256:130328f552dcfac0b1cec75c12e3f005619dc5f874f0a06e8ff7263f0ee6225e"}, + {file = "pytest-7.2.2.tar.gz", hash = "sha256:c99ab0c73aceb050f68929bc93af19ab6db0558791c6a0715723abe9d0ade9d4"}, ] + +[package.dependencies] +attrs = ">=19.2.0" +colorama = {version = "*", markers = "sys_platform == \"win32\""} +exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""} +iniconfig = "*" +packaging = "*" +pluggy = ">=0.12,<2.0" +tomli = {version = ">=1.0.0", markers = "python_version < \"3.11\""} + +[package.extras] +testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "xmlschema"] + +[[package]] +name = "python-dateutil" +version = "2.8.2" +description = "Extensions to the standard Python datetime module" +category = "main" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" +files = [ + {file = "python-dateutil-2.8.2.tar.gz", hash = "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86"}, + {file = "python_dateutil-2.8.2-py2.py3-none-any.whl", hash = "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9"}, +] + +[package.dependencies] +six = ">=1.5" + +[[package]] +name = "pytz" +version = "2022.7.1" +description = "World timezone definitions, modern and historical" +category = "main" +optional = false +python-versions = "*" +files = [ + {file = "pytz-2022.7.1-py2.py3-none-any.whl", hash = "sha256:78f4f37d8198e0627c5f1143240bb0206b8691d8d7ac6d78fee88b78733f8c4a"}, + {file = "pytz-2022.7.1.tar.gz", hash = "sha256:01a0681c4b9684a28304615eba55d1ab31ae00bf68ec157ec3708a8182dbbcd0"}, +] + +[[package]] +name = "requests" +version = "2.28.2" +description = "Python HTTP for Humans." +category = "main" +optional = false +python-versions = ">=3.7, <4" +files = [ + {file = "requests-2.28.2-py3-none-any.whl", hash = "sha256:64299f4909223da747622c030b781c0d7811e359c37124b4bd368fb8c6518baa"}, + {file = "requests-2.28.2.tar.gz", hash = "sha256:98b1b2782e3c6c4904938b84c0eb932721069dfdb9134313beff7c83c2df24bf"}, +] + +[package.dependencies] +certifi = ">=2017.4.17" +charset-normalizer = ">=2,<4" +idna = ">=2.5,<4" +urllib3 = ">=1.21.1,<1.27" + +[package.extras] +socks = ["PySocks (>=1.5.6,!=1.5.7)"] +use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] + +[[package]] +name = "requests-oauthlib" +version = "1.3.1" +description = "OAuthlib authentication support for Requests." +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +files = [ + {file = "requests-oauthlib-1.3.1.tar.gz", hash = "sha256:75beac4a47881eeb94d5ea5d6ad31ef88856affe2332b9aafb52c6452ccf0d7a"}, + {file = "requests_oauthlib-1.3.1-py2.py3-none-any.whl", hash = "sha256:2577c501a2fb8d05a304c09d090d6e47c306fef15809d102b327cf8364bddab5"}, +] + +[package.dependencies] +oauthlib = ">=3.0.0" +requests = ">=2.0.0" + +[package.extras] +rsa = ["oauthlib[signedtoken] (>=3.0.0)"] + +[[package]] +name = "rope" +version = "0.18.0" +description = "a python refactoring library..." +category = "dev" +optional = false +python-versions = "*" +files = [ + {file = "rope-0.18.0.tar.gz", hash = "sha256:786b5c38c530d4846aa68a42604f61b4e69a493390e3ca11b88df0fbfdc3ed04"}, +] + +[package.extras] +dev = ["pytest"] + +[[package]] +name = "rsa" +version = "4.9" +description = "Pure-Python RSA implementation" +category = "main" +optional = false +python-versions = ">=3.6,<4" +files = [ + {file = "rsa-4.9-py3-none-any.whl", hash = "sha256:90260d9058e514786967344d0ef75fa8727eed8a7d2e43ce9f4bcf1b536174f7"}, + {file = "rsa-4.9.tar.gz", hash = "sha256:e38464a49c6c85d7f1351b0126661487a7e0a14a50f1675ec50eb34d4f20ef21"}, +] + +[package.dependencies] +pyasn1 = ">=0.1.3" + +[[package]] +name = "salib" +version = "1.4.7" +description = "Tools for global sensitivity analysis. Contains Sobol', Morris, FAST, DGSM, PAWN, HDMR, Moment Independent and fractional factorial methods" +category = "main" +optional = false +python-versions = ">=3.8" +files = [ + {file = "salib-1.4.7-py3-none-any.whl", hash = "sha256:d04657d9a4972b56ca34ac0f0b1128f21e464d505892adbf921ec70f20662e84"}, + {file = "salib-1.4.7.tar.gz", hash = "sha256:2e6cb19ec772d6cb7368feceae0f61e51f2d6afdbc4f8986a780b87d657b38cc"}, +] + +[package.dependencies] +matplotlib = ">=3.2.2" +multiprocess = "*" +numpy = ">=1.20.3" +pandas = ">=1.1.2" +scipy = ">=1.7.3" + +[package.extras] +dev = ["hatch", "pre-commit", "salib[distributed]", "salib[doc]", "salib[test]"] +distributed = ["pathos (>=0.2.5)"] +doc = ["myst-parser", "numpydoc", "pydata-sphinx-theme (>=0.10)", "sphinx"] +test = ["pytest", "pytest-cov", "salib[distributed]"] + +[[package]] +name = "scikit-learn" +version = "0.23.2" +description = "A set of python modules for machine learning and data mining" +category = "main" +optional = false +python-versions = ">=3.6" +files = [ + {file = "scikit-learn-0.23.2.tar.gz", hash = "sha256:20766f515e6cd6f954554387dfae705d93c7b544ec0e6c6a5d8e006f6f7ef480"}, + {file = "scikit_learn-0.23.2-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:98508723f44c61896a4e15894b2016762a55555fbf09365a0bb1870ecbd442de"}, + {file = "scikit_learn-0.23.2-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:a64817b050efd50f9abcfd311870073e500ae11b299683a519fbb52d85e08d25"}, + {file = "scikit_learn-0.23.2-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:daf276c465c38ef736a79bd79fc80a249f746bcbcae50c40945428f7ece074f8"}, + {file = "scikit_learn-0.23.2-cp36-cp36m-win32.whl", hash = "sha256:cb3e76380312e1f86abd20340ab1d5b3cc46a26f6593d3c33c9ea3e4c7134028"}, + {file = "scikit_learn-0.23.2-cp36-cp36m-win_amd64.whl", hash = "sha256:0a127cc70990d4c15b1019680bfedc7fec6c23d14d3719fdf9b64b22d37cdeca"}, + {file = "scikit_learn-0.23.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:2aa95c2f17d2f80534156215c87bee72b6aa314a7f8b8fe92a2d71f47280570d"}, + {file = "scikit_learn-0.23.2-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:6c28a1d00aae7c3c9568f61aafeaad813f0f01c729bee4fd9479e2132b215c1d"}, + {file = "scikit_learn-0.23.2-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:da8e7c302003dd765d92a5616678e591f347460ac7b53e53d667be7dfe6d1b10"}, + {file = "scikit_learn-0.23.2-cp37-cp37m-win32.whl", hash = "sha256:d9a1ce5f099f29c7c33181cc4386660e0ba891b21a60dc036bf369e3a3ee3aec"}, + {file = "scikit_learn-0.23.2-cp37-cp37m-win_amd64.whl", hash = "sha256:914ac2b45a058d3f1338d7736200f7f3b094857758895f8667be8a81ff443b5b"}, + {file = "scikit_learn-0.23.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:7671bbeddd7f4f9a6968f3b5442dac5f22bf1ba06709ef888cc9132ad354a9ab"}, + {file = "scikit_learn-0.23.2-cp38-cp38-manylinux1_i686.whl", hash = "sha256:d0dcaa54263307075cb93d0bee3ceb02821093b1b3d25f66021987d305d01dce"}, + {file = "scikit_learn-0.23.2-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:5ce7a8021c9defc2b75620571b350acc4a7d9763c25b7593621ef50f3bd019a2"}, + {file = "scikit_learn-0.23.2-cp38-cp38-win32.whl", hash = "sha256:0d39748e7c9669ba648acf40fb3ce96b8a07b240db6888563a7cb76e05e0d9cc"}, + {file = "scikit_learn-0.23.2-cp38-cp38-win_amd64.whl", hash = "sha256:1b8a391de95f6285a2f9adffb7db0892718950954b7149a70c783dc848f104ea"}, +] + +[package.dependencies] +joblib = ">=0.11" +numpy = ">=1.13.3" +scipy = ">=0.19.1" +threadpoolctl = ">=2.0.0" + +[package.extras] +alldeps = ["numpy (>=1.13.3)", "scipy (>=0.19.1)"] + +[[package]] +name = "scipy" +version = "1.10.1" +description = "Fundamental algorithms for scientific computing in Python" +category = "main" +optional = false +python-versions = "<3.12,>=3.8" +files = [ + {file = "scipy-1.10.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e7354fd7527a4b0377ce55f286805b34e8c54b91be865bac273f527e1b839019"}, + {file = "scipy-1.10.1-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:4b3f429188c66603a1a5c549fb414e4d3bdc2a24792e061ffbd607d3d75fd84e"}, + {file = "scipy-1.10.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1553b5dcddd64ba9a0d95355e63fe6c3fc303a8fd77c7bc91e77d61363f7433f"}, + {file = "scipy-1.10.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4c0ff64b06b10e35215abce517252b375e580a6125fd5fdf6421b98efbefb2d2"}, + {file = "scipy-1.10.1-cp310-cp310-win_amd64.whl", hash = "sha256:fae8a7b898c42dffe3f7361c40d5952b6bf32d10c4569098d276b4c547905ee1"}, + {file = "scipy-1.10.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0f1564ea217e82c1bbe75ddf7285ba0709ecd503f048cb1236ae9995f64217bd"}, + {file = "scipy-1.10.1-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:d925fa1c81b772882aa55bcc10bf88324dadb66ff85d548c71515f6689c6dac5"}, + {file = "scipy-1.10.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aaea0a6be54462ec027de54fca511540980d1e9eea68b2d5c1dbfe084797be35"}, + {file = "scipy-1.10.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15a35c4242ec5f292c3dd364a7c71a61be87a3d4ddcc693372813c0b73c9af1d"}, + {file = "scipy-1.10.1-cp311-cp311-win_amd64.whl", hash = "sha256:43b8e0bcb877faf0abfb613d51026cd5cc78918e9530e375727bf0625c82788f"}, + {file = "scipy-1.10.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:5678f88c68ea866ed9ebe3a989091088553ba12c6090244fdae3e467b1139c35"}, + {file = "scipy-1.10.1-cp38-cp38-macosx_12_0_arm64.whl", hash = "sha256:39becb03541f9e58243f4197584286e339029e8908c46f7221abeea4b749fa88"}, + {file = "scipy-1.10.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bce5869c8d68cf383ce240e44c1d9ae7c06078a9396df68ce88a1230f93a30c1"}, + {file = "scipy-1.10.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:07c3457ce0b3ad5124f98a86533106b643dd811dd61b548e78cf4c8786652f6f"}, + {file = "scipy-1.10.1-cp38-cp38-win_amd64.whl", hash = "sha256:049a8bbf0ad95277ffba9b3b7d23e5369cc39e66406d60422c8cfef40ccc8415"}, + {file = "scipy-1.10.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:cd9f1027ff30d90618914a64ca9b1a77a431159df0e2a195d8a9e8a04c78abf9"}, + {file = "scipy-1.10.1-cp39-cp39-macosx_12_0_arm64.whl", hash = "sha256:79c8e5a6c6ffaf3a2262ef1be1e108a035cf4f05c14df56057b64acc5bebffb6"}, + {file = "scipy-1.10.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:51af417a000d2dbe1ec6c372dfe688e041a7084da4fdd350aeb139bd3fb55353"}, + {file = "scipy-1.10.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1b4735d6c28aad3cdcf52117e0e91d6b39acd4272f3f5cd9907c24ee931ad601"}, + {file = "scipy-1.10.1-cp39-cp39-win_amd64.whl", hash = "sha256:7ff7f37b1bf4417baca958d254e8e2875d0cc23aaadbe65b3d5b3077b0eb23ea"}, + {file = "scipy-1.10.1.tar.gz", hash = "sha256:2cf9dfb80a7b4589ba4c40ce7588986d6d5cebc5457cad2c2880f6bc2d42f3a5"}, +] + +[package.dependencies] +numpy = ">=1.19.5,<1.27.0" + +[package.extras] +dev = ["click", "doit (>=0.36.0)", "flake8", "mypy", "pycodestyle", "pydevtool", "rich-click", "typing_extensions"] +doc = ["matplotlib (>2)", "numpydoc", "pydata-sphinx-theme (==0.9.0)", "sphinx (!=4.1.0)", "sphinx-design (>=0.2.0)"] +test = ["asv", "gmpy2", "mpmath", "pooch", "pytest", "pytest-cov", "pytest-timeout", "pytest-xdist", "scikit-umfpack", "threadpoolctl"] + +[[package]] +name = "seaborn" +version = "0.11.2" +description = "seaborn: statistical data visualization" +category = "main" +optional = false +python-versions = ">=3.6" +files = [ + {file = "seaborn-0.11.2-py3-none-any.whl", hash = "sha256:85a6baa9b55f81a0623abddc4a26b334653ff4c6b18c418361de19dbba0ef283"}, + {file = "seaborn-0.11.2.tar.gz", hash = "sha256:cf45e9286d40826864be0e3c066f98536982baf701a7caa386511792d61ff4f6"}, +] + +[package.dependencies] +matplotlib = ">=2.2" +numpy = ">=1.15" +pandas = ">=0.23" +scipy = ">=1.0" + +[[package]] +name = "setuptools" +version = "67.6.0" +description = "Easily download, build, install, upgrade, and uninstall Python packages" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "setuptools-67.6.0-py3-none-any.whl", hash = "sha256:b78aaa36f6b90a074c1fa651168723acbf45d14cb1196b6f02c0fd07f17623b2"}, + {file = "setuptools-67.6.0.tar.gz", hash = "sha256:2ee892cd5f29f3373097f5a814697e397cf3ce313616df0af11231e2ad118077"}, +] + +[package.extras] +docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-hoverxref (<2)", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (==0.8.3)", "sphinx-reredirects", "sphinxcontrib-towncrier"] +testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8 (<5)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pip (>=19.1)", "pip-run (>=8.8)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] +testing-integration = ["build[virtualenv]", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] + +[[package]] +name = "six" +version = "1.16.0" +description = "Python 2 and 3 compatibility utilities" +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" +files = [ + {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, + {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, +] + +[[package]] +name = "snowballstemmer" +version = "2.2.0" +description = "This package provides 29 stemmers for 28 languages generated from Snowball algorithms." +category = "dev" +optional = false +python-versions = "*" +files = [ + {file = "snowballstemmer-2.2.0-py2.py3-none-any.whl", hash = "sha256:c8e1716e83cc398ae16824e5572ae04e0d9fc2c6b985fb0f900f5f0c96ecba1a"}, + {file = "snowballstemmer-2.2.0.tar.gz", hash = "sha256:09b16deb8547d3412ad7b590689584cd0fe25ec8db3be37788be3810cbf19cb1"}, +] + +[[package]] +name = "sphinx" +version = "3.5.4" +description = "Python documentation generator" +category = "dev" +optional = false +python-versions = ">=3.5" +files = [ + {file = "Sphinx-3.5.4-py3-none-any.whl", hash = "sha256:2320d4e994a191f4b4be27da514e46b3d6b420f2ff895d064f52415d342461e8"}, + {file = "Sphinx-3.5.4.tar.gz", hash = "sha256:19010b7b9fa0dc7756a6e105b2aacd3a80f798af3c25c273be64d7beeb482cb1"}, +] + +[package.dependencies] +alabaster = ">=0.7,<0.8" +babel = ">=1.3" +colorama = {version = ">=0.3.5", markers = "sys_platform == \"win32\""} +docutils = ">=0.12,<0.17" +imagesize = "*" +Jinja2 = ">=2.3" +packaging = "*" +Pygments = ">=2.0" +requests = ">=2.5.0" +setuptools = "*" +snowballstemmer = ">=1.1" +sphinxcontrib-applehelp = "*" +sphinxcontrib-devhelp = "*" +sphinxcontrib-htmlhelp = "*" +sphinxcontrib-jsmath = "*" +sphinxcontrib-qthelp = "*" +sphinxcontrib-serializinghtml = "*" + +[package.extras] +docs = ["sphinxcontrib-websupport"] +lint = ["docutils-stubs", "flake8 (>=3.5.0)", "isort", "mypy (>=0.800)"] +test = ["cython", "html5lib", "pytest", "pytest-cov", "typed-ast"] + +[[package]] +name = "sphinx-rtd-theme" +version = "0.5.2" +description = "Read the Docs theme for Sphinx" +category = "dev" +optional = false +python-versions = "*" +files = [ + {file = "sphinx_rtd_theme-0.5.2-py2.py3-none-any.whl", hash = "sha256:4a05bdbe8b1446d77a01e20a23ebc6777c74f43237035e76be89699308987d6f"}, + {file = "sphinx_rtd_theme-0.5.2.tar.gz", hash = "sha256:32bd3b5d13dc8186d7a42fc816a23d32e83a4827d7d9882948e7b837c232da5a"}, +] + +[package.dependencies] +docutils = "<0.17" +sphinx = "*" + +[package.extras] +dev = ["bump2version", "sphinxcontrib-httpdomain", "transifex-client"] + +[[package]] +name = "sphinxcontrib-applehelp" +version = "1.0.4" +description = "sphinxcontrib-applehelp is a Sphinx extension which outputs Apple help books" +category = "dev" +optional = false +python-versions = ">=3.8" +files = [ + {file = "sphinxcontrib-applehelp-1.0.4.tar.gz", hash = "sha256:828f867945bbe39817c210a1abfd1bc4895c8b73fcaade56d45357a348a07d7e"}, + {file = "sphinxcontrib_applehelp-1.0.4-py3-none-any.whl", hash = "sha256:29d341f67fb0f6f586b23ad80e072c8e6ad0b48417db2bde114a4c9746feb228"}, +] + +[package.extras] +lint = ["docutils-stubs", "flake8", "mypy"] +test = ["pytest"] + +[[package]] +name = "sphinxcontrib-devhelp" +version = "1.0.2" +description = "sphinxcontrib-devhelp is a sphinx extension which outputs Devhelp document." +category = "dev" +optional = false +python-versions = ">=3.5" +files = [ + {file = "sphinxcontrib-devhelp-1.0.2.tar.gz", hash = "sha256:ff7f1afa7b9642e7060379360a67e9c41e8f3121f2ce9164266f61b9f4b338e4"}, + {file = "sphinxcontrib_devhelp-1.0.2-py2.py3-none-any.whl", hash = "sha256:8165223f9a335cc1af7ffe1ed31d2871f325254c0423bc0c4c7cd1c1e4734a2e"}, +] + +[package.extras] +lint = ["docutils-stubs", "flake8", "mypy"] +test = ["pytest"] + +[[package]] +name = "sphinxcontrib-htmlhelp" +version = "2.0.1" +description = "sphinxcontrib-htmlhelp is a sphinx extension which renders HTML help files" +category = "dev" +optional = false +python-versions = ">=3.8" +files = [ + {file = "sphinxcontrib-htmlhelp-2.0.1.tar.gz", hash = "sha256:0cbdd302815330058422b98a113195c9249825d681e18f11e8b1f78a2f11efff"}, + {file = "sphinxcontrib_htmlhelp-2.0.1-py3-none-any.whl", hash = "sha256:c38cb46dccf316c79de6e5515e1770414b797162b23cd3d06e67020e1d2a6903"}, +] + +[package.extras] +lint = ["docutils-stubs", "flake8", "mypy"] +test = ["html5lib", "pytest"] + +[[package]] +name = "sphinxcontrib-jsmath" +version = "1.0.1" +description = "A sphinx extension which renders display math in HTML via JavaScript" +category = "dev" +optional = false +python-versions = ">=3.5" +files = [ + {file = "sphinxcontrib-jsmath-1.0.1.tar.gz", hash = "sha256:a9925e4a4587247ed2191a22df5f6970656cb8ca2bd6284309578f2153e0c4b8"}, + {file = "sphinxcontrib_jsmath-1.0.1-py2.py3-none-any.whl", hash = "sha256:2ec2eaebfb78f3f2078e73666b1415417a116cc848b72e5172e596c871103178"}, +] + +[package.extras] +test = ["flake8", "mypy", "pytest"] + +[[package]] +name = "sphinxcontrib-napoleon" +version = "0.7" +description = "Sphinx \"napoleon\" extension." +category = "dev" +optional = false +python-versions = "*" +files = [ + {file = "sphinxcontrib-napoleon-0.7.tar.gz", hash = "sha256:407382beed396e9f2d7f3043fad6afda95719204a1e1a231ac865f40abcbfcf8"}, + {file = "sphinxcontrib_napoleon-0.7-py2.py3-none-any.whl", hash = "sha256:711e41a3974bdf110a484aec4c1a556799eb0b3f3b897521a018ad7e2db13fef"}, +] + +[package.dependencies] +pockets = ">=0.3" +six = ">=1.5.2" + +[[package]] +name = "sphinxcontrib-qthelp" +version = "1.0.3" +description = "sphinxcontrib-qthelp is a sphinx extension which outputs QtHelp document." +category = "dev" +optional = false +python-versions = ">=3.5" +files = [ + {file = "sphinxcontrib-qthelp-1.0.3.tar.gz", hash = "sha256:4c33767ee058b70dba89a6fc5c1892c0d57a54be67ddd3e7875a18d14cba5a72"}, + {file = "sphinxcontrib_qthelp-1.0.3-py2.py3-none-any.whl", hash = "sha256:bd9fc24bcb748a8d51fd4ecaade681350aa63009a347a8c14e637895444dfab6"}, +] + +[package.extras] +lint = ["docutils-stubs", "flake8", "mypy"] +test = ["pytest"] + +[[package]] +name = "sphinxcontrib-serializinghtml" +version = "1.1.5" +description = "sphinxcontrib-serializinghtml is a sphinx extension which outputs \"serialized\" HTML files (json and pickle)." +category = "dev" +optional = false +python-versions = ">=3.5" +files = [ + {file = "sphinxcontrib-serializinghtml-1.1.5.tar.gz", hash = "sha256:aa5f6de5dfdf809ef505c4895e51ef5c9eac17d0f287933eb49ec495280b6952"}, + {file = "sphinxcontrib_serializinghtml-1.1.5-py2.py3-none-any.whl", hash = "sha256:352a9a00ae864471d3a7ead8d7d79f5fc0b57e8b3f95e9867eb9eb28999b92fd"}, +] + +[package.extras] +lint = ["docutils-stubs", "flake8", "mypy"] +test = ["pytest"] + +[[package]] +name = "tabulate" +version = "0.8.10" +description = "Pretty-print tabular data" +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +files = [ + {file = "tabulate-0.8.10-py3-none-any.whl", hash = "sha256:0ba055423dbaa164b9e456abe7920c5e8ed33fcc16f6d1b2f2d152c8e1e8b4fc"}, + {file = "tabulate-0.8.10.tar.gz", hash = "sha256:6c57f3f3dd7ac2782770155f3adb2db0b1a269637e42f27599925e64b114f519"}, +] + +[package.extras] +widechars = ["wcwidth"] + +[[package]] +name = "tensorboard" +version = "2.11.2" +description = "TensorBoard lets you watch Tensors Flow" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "tensorboard-2.11.2-py3-none-any.whl", hash = "sha256:cbaa2210c375f3af1509f8571360a19ccc3ded1d9641533414874b5deca47e89"}, +] + +[package.dependencies] +absl-py = ">=0.4" +google-auth = ">=1.6.3,<3" +google-auth-oauthlib = ">=0.4.1,<0.5" +grpcio = ">=1.24.3" +markdown = ">=2.6.8" +numpy = ">=1.12.0" +protobuf = ">=3.9.2,<4" +requests = ">=2.21.0,<3" +setuptools = ">=41.0.0" +tensorboard-data-server = ">=0.6.0,<0.7.0" +tensorboard-plugin-wit = ">=1.6.0" +werkzeug = ">=1.0.1" +wheel = ">=0.26" + +[[package]] +name = "tensorboard-data-server" +version = "0.6.1" +description = "Fast data loading for TensorBoard" +category = "main" +optional = false +python-versions = ">=3.6" +files = [ + {file = "tensorboard_data_server-0.6.1-py3-none-any.whl", hash = "sha256:809fe9887682d35c1f7d1f54f0f40f98bb1f771b14265b453ca051e2ce58fca7"}, + {file = "tensorboard_data_server-0.6.1-py3-none-macosx_10_9_x86_64.whl", hash = "sha256:fa8cef9be4fcae2f2363c88176638baf2da19c5ec90addb49b1cde05c95c88ee"}, + {file = "tensorboard_data_server-0.6.1-py3-none-manylinux2010_x86_64.whl", hash = "sha256:d8237580755e58eff68d1f3abefb5b1e39ae5c8b127cc40920f9c4fb33f4b98a"}, +] + +[[package]] +name = "tensorboard-plugin-wit" +version = "1.8.1" +description = "What-If Tool TensorBoard plugin." +category = "main" +optional = false +python-versions = "*" +files = [ + {file = "tensorboard_plugin_wit-1.8.1-py3-none-any.whl", hash = "sha256:ff26bdd583d155aa951ee3b152b3d0cffae8005dc697f72b44a8e8c2a77a8cbe"}, +] + +[[package]] +name = "tensorflow" +version = "2.11.0" +description = "TensorFlow is an open source machine learning framework for everyone." +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "tensorflow-2.11.0-cp310-cp310-macosx_10_14_x86_64.whl", hash = "sha256:6c049fec6c2040685d6f43a63e17ccc5d6b0abc16b70cc6f5e7d691262b5d2d0"}, + {file = "tensorflow-2.11.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bcc8380820cea8f68f6c90b8aee5432e8537e5bb9ec79ac61a98e6a9a02c7d40"}, + {file = "tensorflow-2.11.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d973458241c8771bf95d4ba68ad5d67b094f72dd181c2d562ffab538c1b0dad7"}, + {file = "tensorflow-2.11.0-cp310-cp310-win_amd64.whl", hash = "sha256:d470b772ee3c291a8c7be2331e7c379e0c338223c0bf532f5906d4556f17580d"}, + {file = "tensorflow-2.11.0-cp37-cp37m-macosx_10_14_x86_64.whl", hash = "sha256:d29c1179149fa469ad68234c52c83081d037ead243f90e826074e2563a0f938a"}, + {file = "tensorflow-2.11.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2cdba2fce00d6c924470d4fb65d5e95a4b6571a863860608c0c13f0393f4ca0d"}, + {file = "tensorflow-2.11.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4f2ab20f93d2b52a44b414ec6dcf82aa12110e90e0920039a27108de28ae2728"}, + {file = "tensorflow-2.11.0-cp37-cp37m-win_amd64.whl", hash = "sha256:445510f092f7827e1f60f59b8bfb58e664aaf05d07daaa21c5735a7f76ca2b25"}, + {file = "tensorflow-2.11.0-cp38-cp38-macosx_10_14_x86_64.whl", hash = "sha256:056d29f2212342536ce3856aa47910a2515eb97ec0a6cc29ed47fc4be1369ec8"}, + {file = "tensorflow-2.11.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:17b29d6d360fad545ab1127db52592efd3f19ac55c1a45e5014da328ae867ab4"}, + {file = "tensorflow-2.11.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:335ab5cccd7a1c46e3d89d9d46913f0715e8032df8d7438f9743b3fb97b39f69"}, + {file = "tensorflow-2.11.0-cp38-cp38-win_amd64.whl", hash = "sha256:d48da37c8ae711eb38047a56a052ca8bb4ee018a91a479e42b7a8d117628c32e"}, + {file = "tensorflow-2.11.0-cp39-cp39-macosx_10_14_x86_64.whl", hash = "sha256:d9cf25bca641f2e5c77caa3bfd8dd6b892a7aec0695c54d2a7c9f52a54a8d487"}, + {file = "tensorflow-2.11.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0d28f9691ebc48c0075e271023b3f147ae2bc29a3d3a7f42d45019c6b4a700d2"}, + {file = "tensorflow-2.11.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:276a44210d956701899dc78ad0aa116a0071f22fb0bcc1ea6bb59f7646b08d11"}, + {file = "tensorflow-2.11.0-cp39-cp39-win_amd64.whl", hash = "sha256:cc3444fe1d58c65a195a69656bf56015bf19dc2916da607d784b0a1e215ec008"}, +] + +[package.dependencies] +absl-py = ">=1.0.0" +astunparse = ">=1.6.0" +flatbuffers = ">=2.0" +gast = ">=0.2.1,<=0.4.0" +google-pasta = ">=0.1.1" +grpcio = ">=1.24.3,<2.0" +h5py = ">=2.9.0" +keras = ">=2.11.0,<2.12" +libclang = ">=13.0.0" +numpy = ">=1.20" +opt-einsum = ">=2.3.2" +packaging = "*" +protobuf = ">=3.9.2,<3.20" +setuptools = "*" +six = ">=1.12.0" +tensorboard = ">=2.11,<2.12" +tensorflow-estimator = ">=2.11.0,<2.12" +tensorflow-io-gcs-filesystem = {version = ">=0.23.1", markers = "platform_machine != \"arm64\" or platform_system != \"Darwin\""} +termcolor = ">=1.1.0" +typing-extensions = ">=3.6.6" +wrapt = ">=1.11.0" + +[[package]] +name = "tensorflow-estimator" +version = "2.11.0" +description = "TensorFlow Estimator." +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "tensorflow_estimator-2.11.0-py2.py3-none-any.whl", hash = "sha256:ea3b64acfff3d9a244f06178c9bdedcbdd3f125b67d0888dba8229498d06468b"}, +] + +[[package]] +name = "tensorflow-io-gcs-filesystem" +version = "0.31.0" +description = "TensorFlow IO" +category = "main" +optional = false +python-versions = ">=3.7, <3.12" +files = [ + {file = "tensorflow_io_gcs_filesystem-0.31.0-cp310-cp310-macosx_10_14_x86_64.whl", hash = "sha256:a71421f8d75a093b6aac65b4c8c8d2f768c3ca6215307cf8c16192e62d992bcf"}, + {file = "tensorflow_io_gcs_filesystem-0.31.0-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:359134ecbd3bf938bb0cf65be4526106c30da461b2e2ce05446a229ed35f6832"}, + {file = "tensorflow_io_gcs_filesystem-0.31.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b658b33567552f155af2ed848130f787bfda29381fa78cd905d5ee8254364f3c"}, + {file = "tensorflow_io_gcs_filesystem-0.31.0-cp310-cp310-win_amd64.whl", hash = "sha256:961353b38c76471fa296bb7d883322c66b91415e7d47087236a6706db3ab2758"}, + {file = "tensorflow_io_gcs_filesystem-0.31.0-cp311-cp311-macosx_10_14_x86_64.whl", hash = "sha256:8909c4344b0e96aa356230ab460ffafe5900c33c1aaced65fafae71d177a1966"}, + {file = "tensorflow_io_gcs_filesystem-0.31.0-cp311-cp311-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:e417faf8755aafe52d8f8c6b5ae5bae6e4fae8326ee3acd5e9181b83bbfbae87"}, + {file = "tensorflow_io_gcs_filesystem-0.31.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:37c40e3c4ee1f8dda3b545deea6b8839192c82037d8021db9f589908034ad975"}, + {file = "tensorflow_io_gcs_filesystem-0.31.0-cp311-cp311-win_amd64.whl", hash = "sha256:4bb37d23f21c434687b11059cb7ffd094d52a7813368915ba1b7057e3c16e414"}, + {file = "tensorflow_io_gcs_filesystem-0.31.0-cp37-cp37m-macosx_10_14_x86_64.whl", hash = "sha256:a7e8d4bd0a25de7637e562997c011294d7ea595a76f315427a5dd522d56e9d49"}, + {file = "tensorflow_io_gcs_filesystem-0.31.0-cp37-cp37m-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:fbcfb4aa2eaa9a3038d2487e570ff93feb1dbe51c3a4663d7d9ab9f9a9f9a9d8"}, + {file = "tensorflow_io_gcs_filesystem-0.31.0-cp37-cp37m-win_amd64.whl", hash = "sha256:e3933059b1c53e062075de2e355ec136b655da5883c3c26736c45dfeb1901945"}, + {file = "tensorflow_io_gcs_filesystem-0.31.0-cp38-cp38-macosx_10_14_x86_64.whl", hash = "sha256:f0adfbcd264262797d429311843733da2d5c1ffb119fbfa6339269b6c0414113"}, + {file = "tensorflow_io_gcs_filesystem-0.31.0-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:20e3ee5df01f2bd81d37fc715816c329b7533ccca967c47946eb458a5b7a7280"}, + {file = "tensorflow_io_gcs_filesystem-0.31.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bd628609b77aee0e385eadf1628222486f19b8f1d81b5f0a344f2470204df116"}, + {file = "tensorflow_io_gcs_filesystem-0.31.0-cp38-cp38-win_amd64.whl", hash = "sha256:b4ebb30ad7ce5f3769e3d959ea99bd95d80a44099bcf94da6042f9755ac6e850"}, + {file = "tensorflow_io_gcs_filesystem-0.31.0-cp39-cp39-macosx_10_14_x86_64.whl", hash = "sha256:68b89ef9f63f297de1cd9d545bc45dddc7d8fe12bcda4266279b244e8cf3b7c0"}, + {file = "tensorflow_io_gcs_filesystem-0.31.0-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:e6d8cc7b14ade870168b9704ee44f9c55b468b9a00ed40e12d20fffd321193b5"}, + {file = "tensorflow_io_gcs_filesystem-0.31.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:97ebb9a8001a38f615aa1f90d2e998b7bd6eddae7aafc92897833610b039401b"}, + {file = "tensorflow_io_gcs_filesystem-0.31.0-cp39-cp39-win_amd64.whl", hash = "sha256:cb7459c15608fe42973a78e4d3ad7ac79cfc7adae1ccb1b1846db3165fbc081a"}, +] + +[package.extras] +tensorflow = ["tensorflow (>=2.11.0,<2.12.0)"] +tensorflow-aarch64 = ["tensorflow-aarch64 (>=2.11.0,<2.12.0)"] +tensorflow-cpu = ["tensorflow-cpu (>=2.11.0,<2.12.0)"] +tensorflow-gpu = ["tensorflow-gpu (>=2.11.0,<2.12.0)"] +tensorflow-rocm = ["tensorflow-rocm (>=2.11.0,<2.12.0)"] + +[[package]] +name = "termcolor" +version = "2.2.0" +description = "ANSI color formatting for output in terminal" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "termcolor-2.2.0-py3-none-any.whl", hash = "sha256:91ddd848e7251200eac969846cbae2dacd7d71c2871e92733289e7e3666f48e7"}, + {file = "termcolor-2.2.0.tar.gz", hash = "sha256:dfc8ac3f350788f23b2947b3e6cfa5a53b630b612e6cd8965a015a776020b99a"}, +] + +[package.extras] +tests = ["pytest", "pytest-cov"] + +[[package]] +name = "threadpoolctl" +version = "3.1.0" +description = "threadpoolctl" +category = "main" +optional = false +python-versions = ">=3.6" +files = [ + {file = "threadpoolctl-3.1.0-py3-none-any.whl", hash = "sha256:8b99adda265feb6773280df41eece7b2e6561b772d21ffd52e372f999024907b"}, + {file = "threadpoolctl-3.1.0.tar.gz", hash = "sha256:a335baacfaa4400ae1f0d8e3a58d6674d2f8828e3716bb2802c44955ad391380"}, +] + +[[package]] +name = "tomli" +version = "2.0.1" +description = "A lil' TOML parser" +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, + {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, +] + +[[package]] +name = "tqdm" +version = "4.65.0" +description = "Fast, Extensible Progress Meter" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "tqdm-4.65.0-py3-none-any.whl", hash = "sha256:c4f53a17fe37e132815abceec022631be8ffe1b9381c2e6e30aa70edc99e9671"}, + {file = "tqdm-4.65.0.tar.gz", hash = "sha256:1871fb68a86b8fb3b59ca4cdd3dcccbc7e6d613eeed31f4c332531977b89beb5"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "platform_system == \"Windows\""} + +[package.extras] +dev = ["py-make (>=0.1.0)", "twine", "wheel"] +notebook = ["ipywidgets (>=6)"] +slack = ["slack-sdk"] +telegram = ["requests"] + +[[package]] +name = "types-tabulate" +version = "0.9.0.1" +description = "Typing stubs for tabulate" +category = "main" +optional = false +python-versions = "*" +files = [ + {file = "types-tabulate-0.9.0.1.tar.gz", hash = "sha256:e486292c279f19247865bdabe802419740a0e74b53444e7f7a8009e08129da5d"}, + {file = "types_tabulate-0.9.0.1-py3-none-any.whl", hash = "sha256:be2ea0de05f615ccfcbadf6206aa720e265955eb1de23e343aec9d8bf3fa9aaa"}, +] + +[[package]] +name = "typing-extensions" +version = "4.5.0" +description = "Backported and Experimental Type Hints for Python 3.7+" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "typing_extensions-4.5.0-py3-none-any.whl", hash = "sha256:fb33085c39dd998ac16d1431ebc293a8b3eedd00fd4a32de0ff79002c19511b4"}, + {file = "typing_extensions-4.5.0.tar.gz", hash = "sha256:5cb5f4a79139d699607b3ef622a1dedafa84e115ab0024e0d9c044a9479ca7cb"}, +] + +[[package]] +name = "urllib3" +version = "1.26.15" +description = "HTTP library with thread-safe connection pooling, file post, and more." +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" +files = [ + {file = "urllib3-1.26.15-py2.py3-none-any.whl", hash = "sha256:aa751d169e23c7479ce47a0cb0da579e3ede798f994f5816a74e4f4500dcea42"}, + {file = "urllib3-1.26.15.tar.gz", hash = "sha256:8a388717b9476f934a21484e8c8e61875ab60644d29b9b39e11e4b9dc1c6b305"}, +] + +[package.extras] +brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)", "brotlipy (>=0.6.0)"] +secure = ["certifi", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "ipaddress", "pyOpenSSL (>=0.14)", "urllib3-secure-extra"] +socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] + +[[package]] +name = "werkzeug" +version = "2.2.3" +description = "The comprehensive WSGI web application library." +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "Werkzeug-2.2.3-py3-none-any.whl", hash = "sha256:56433961bc1f12533306c624f3be5e744389ac61d722175d543e1751285da612"}, + {file = "Werkzeug-2.2.3.tar.gz", hash = "sha256:2e1ccc9417d4da358b9de6f174e3ac094391ea1d4fbef2d667865d819dfd0afe"}, +] + +[package.dependencies] +MarkupSafe = ">=2.1.1" + +[package.extras] +watchdog = ["watchdog"] + +[[package]] +name = "wheel" +version = "0.40.0" +description = "A built-package format for Python" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "wheel-0.40.0-py3-none-any.whl", hash = "sha256:d236b20e7cb522daf2390fa84c55eea81c5c30190f90f29ae2ca1ad8355bf247"}, + {file = "wheel-0.40.0.tar.gz", hash = "sha256:cd1196f3faee2b31968d626e1731c94f99cbdb67cf5a46e4f5656cbee7738873"}, +] + +[package.extras] +test = ["pytest (>=6.0.0)"] + +[[package]] +name = "wrapt" +version = "1.15.0" +description = "Module for decorators, wrappers and monkey patching." +category = "main" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" +files = [ + {file = "wrapt-1.15.0-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:ca1cccf838cd28d5a0883b342474c630ac48cac5df0ee6eacc9c7290f76b11c1"}, + {file = "wrapt-1.15.0-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:e826aadda3cae59295b95343db8f3d965fb31059da7de01ee8d1c40a60398b29"}, + {file = "wrapt-1.15.0-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:5fc8e02f5984a55d2c653f5fea93531e9836abbd84342c1d1e17abc4a15084c2"}, + {file = "wrapt-1.15.0-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:96e25c8603a155559231c19c0349245eeb4ac0096fe3c1d0be5c47e075bd4f46"}, + {file = "wrapt-1.15.0-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:40737a081d7497efea35ab9304b829b857f21558acfc7b3272f908d33b0d9d4c"}, + {file = "wrapt-1.15.0-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:f87ec75864c37c4c6cb908d282e1969e79763e0d9becdfe9fe5473b7bb1e5f09"}, + {file = "wrapt-1.15.0-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:1286eb30261894e4c70d124d44b7fd07825340869945c79d05bda53a40caa079"}, + {file = "wrapt-1.15.0-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:493d389a2b63c88ad56cdc35d0fa5752daac56ca755805b1b0c530f785767d5e"}, + {file = "wrapt-1.15.0-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:58d7a75d731e8c63614222bcb21dd992b4ab01a399f1f09dd82af17bbfc2368a"}, + {file = "wrapt-1.15.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:21f6d9a0d5b3a207cdf7acf8e58d7d13d463e639f0c7e01d82cdb671e6cb7923"}, + {file = "wrapt-1.15.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ce42618f67741d4697684e501ef02f29e758a123aa2d669e2d964ff734ee00ee"}, + {file = "wrapt-1.15.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:41d07d029dd4157ae27beab04d22b8e261eddfc6ecd64ff7000b10dc8b3a5727"}, + {file = "wrapt-1.15.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:54accd4b8bc202966bafafd16e69da9d5640ff92389d33d28555c5fd4f25ccb7"}, + {file = "wrapt-1.15.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2fbfbca668dd15b744418265a9607baa970c347eefd0db6a518aaf0cfbd153c0"}, + {file = "wrapt-1.15.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:76e9c727a874b4856d11a32fb0b389afc61ce8aaf281ada613713ddeadd1cfec"}, + {file = "wrapt-1.15.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e20076a211cd6f9b44a6be58f7eeafa7ab5720eb796975d0c03f05b47d89eb90"}, + {file = "wrapt-1.15.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a74d56552ddbde46c246b5b89199cb3fd182f9c346c784e1a93e4dc3f5ec9975"}, + {file = "wrapt-1.15.0-cp310-cp310-win32.whl", hash = "sha256:26458da5653aa5b3d8dc8b24192f574a58984c749401f98fff994d41d3f08da1"}, + {file = "wrapt-1.15.0-cp310-cp310-win_amd64.whl", hash = "sha256:75760a47c06b5974aa5e01949bf7e66d2af4d08cb8c1d6516af5e39595397f5e"}, + {file = "wrapt-1.15.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ba1711cda2d30634a7e452fc79eabcadaffedf241ff206db2ee93dd2c89a60e7"}, + {file = "wrapt-1.15.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:56374914b132c702aa9aa9959c550004b8847148f95e1b824772d453ac204a72"}, + {file = "wrapt-1.15.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a89ce3fd220ff144bd9d54da333ec0de0399b52c9ac3d2ce34b569cf1a5748fb"}, + {file = "wrapt-1.15.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3bbe623731d03b186b3d6b0d6f51865bf598587c38d6f7b0be2e27414f7f214e"}, + {file = "wrapt-1.15.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3abbe948c3cbde2689370a262a8d04e32ec2dd4f27103669a45c6929bcdbfe7c"}, + {file = "wrapt-1.15.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:b67b819628e3b748fd3c2192c15fb951f549d0f47c0449af0764d7647302fda3"}, + {file = "wrapt-1.15.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:7eebcdbe3677e58dd4c0e03b4f2cfa346ed4049687d839adad68cc38bb559c92"}, + {file = "wrapt-1.15.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:74934ebd71950e3db69960a7da29204f89624dde411afbfb3b4858c1409b1e98"}, + {file = "wrapt-1.15.0-cp311-cp311-win32.whl", hash = "sha256:bd84395aab8e4d36263cd1b9308cd504f6cf713b7d6d3ce25ea55670baec5416"}, + {file = "wrapt-1.15.0-cp311-cp311-win_amd64.whl", hash = "sha256:a487f72a25904e2b4bbc0817ce7a8de94363bd7e79890510174da9d901c38705"}, + {file = "wrapt-1.15.0-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:4ff0d20f2e670800d3ed2b220d40984162089a6e2c9646fdb09b85e6f9a8fc29"}, + {file = "wrapt-1.15.0-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:9ed6aa0726b9b60911f4aed8ec5b8dd7bf3491476015819f56473ffaef8959bd"}, + {file = "wrapt-1.15.0-cp35-cp35m-manylinux2010_i686.whl", hash = "sha256:896689fddba4f23ef7c718279e42f8834041a21342d95e56922e1c10c0cc7afb"}, + {file = "wrapt-1.15.0-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:75669d77bb2c071333417617a235324a1618dba66f82a750362eccbe5b61d248"}, + {file = "wrapt-1.15.0-cp35-cp35m-win32.whl", hash = "sha256:fbec11614dba0424ca72f4e8ba3c420dba07b4a7c206c8c8e4e73f2e98f4c559"}, + {file = "wrapt-1.15.0-cp35-cp35m-win_amd64.whl", hash = "sha256:fd69666217b62fa5d7c6aa88e507493a34dec4fa20c5bd925e4bc12fce586639"}, + {file = "wrapt-1.15.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:b0724f05c396b0a4c36a3226c31648385deb6a65d8992644c12a4963c70326ba"}, + {file = "wrapt-1.15.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bbeccb1aa40ab88cd29e6c7d8585582c99548f55f9b2581dfc5ba68c59a85752"}, + {file = "wrapt-1.15.0-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:38adf7198f8f154502883242f9fe7333ab05a5b02de7d83aa2d88ea621f13364"}, + {file = "wrapt-1.15.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:578383d740457fa790fdf85e6d346fda1416a40549fe8db08e5e9bd281c6a475"}, + {file = "wrapt-1.15.0-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:a4cbb9ff5795cd66f0066bdf5947f170f5d63a9274f99bdbca02fd973adcf2a8"}, + {file = "wrapt-1.15.0-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:af5bd9ccb188f6a5fdda9f1f09d9f4c86cc8a539bd48a0bfdc97723970348418"}, + {file = "wrapt-1.15.0-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:b56d5519e470d3f2fe4aa7585f0632b060d532d0696c5bdfb5e8319e1d0f69a2"}, + {file = "wrapt-1.15.0-cp36-cp36m-win32.whl", hash = "sha256:77d4c1b881076c3ba173484dfa53d3582c1c8ff1f914c6461ab70c8428b796c1"}, + {file = "wrapt-1.15.0-cp36-cp36m-win_amd64.whl", hash = "sha256:077ff0d1f9d9e4ce6476c1a924a3332452c1406e59d90a2cf24aeb29eeac9420"}, + {file = "wrapt-1.15.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:5c5aa28df055697d7c37d2099a7bc09f559d5053c3349b1ad0c39000e611d317"}, + {file = "wrapt-1.15.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3a8564f283394634a7a7054b7983e47dbf39c07712d7b177b37e03f2467a024e"}, + {file = "wrapt-1.15.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:780c82a41dc493b62fc5884fb1d3a3b81106642c5c5c78d6a0d4cbe96d62ba7e"}, + {file = "wrapt-1.15.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e169e957c33576f47e21864cf3fc9ff47c223a4ebca8960079b8bd36cb014fd0"}, + {file = "wrapt-1.15.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:b02f21c1e2074943312d03d243ac4388319f2456576b2c6023041c4d57cd7019"}, + {file = "wrapt-1.15.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:f2e69b3ed24544b0d3dbe2c5c0ba5153ce50dcebb576fdc4696d52aa22db6034"}, + {file = "wrapt-1.15.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:d787272ed958a05b2c86311d3a4135d3c2aeea4fc655705f074130aa57d71653"}, + {file = "wrapt-1.15.0-cp37-cp37m-win32.whl", hash = "sha256:02fce1852f755f44f95af51f69d22e45080102e9d00258053b79367d07af39c0"}, + {file = "wrapt-1.15.0-cp37-cp37m-win_amd64.whl", hash = "sha256:abd52a09d03adf9c763d706df707c343293d5d106aea53483e0ec8d9e310ad5e"}, + {file = "wrapt-1.15.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:cdb4f085756c96a3af04e6eca7f08b1345e94b53af8921b25c72f096e704e145"}, + {file = "wrapt-1.15.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:230ae493696a371f1dbffaad3dafbb742a4d27a0afd2b1aecebe52b740167e7f"}, + {file = "wrapt-1.15.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:63424c681923b9f3bfbc5e3205aafe790904053d42ddcc08542181a30a7a51bd"}, + {file = "wrapt-1.15.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d6bcbfc99f55655c3d93feb7ef3800bd5bbe963a755687cbf1f490a71fb7794b"}, + {file = "wrapt-1.15.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c99f4309f5145b93eca6e35ac1a988f0dc0a7ccf9ccdcd78d3c0adf57224e62f"}, + {file = "wrapt-1.15.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:b130fe77361d6771ecf5a219d8e0817d61b236b7d8b37cc045172e574ed219e6"}, + {file = "wrapt-1.15.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:96177eb5645b1c6985f5c11d03fc2dbda9ad24ec0f3a46dcce91445747e15094"}, + {file = "wrapt-1.15.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:d5fe3e099cf07d0fb5a1e23d399e5d4d1ca3e6dfcbe5c8570ccff3e9208274f7"}, + {file = "wrapt-1.15.0-cp38-cp38-win32.whl", hash = "sha256:abd8f36c99512755b8456047b7be10372fca271bf1467a1caa88db991e7c421b"}, + {file = "wrapt-1.15.0-cp38-cp38-win_amd64.whl", hash = "sha256:b06fa97478a5f478fb05e1980980a7cdf2712015493b44d0c87606c1513ed5b1"}, + {file = "wrapt-1.15.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:2e51de54d4fb8fb50d6ee8327f9828306a959ae394d3e01a1ba8b2f937747d86"}, + {file = "wrapt-1.15.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:0970ddb69bba00670e58955f8019bec4a42d1785db3faa043c33d81de2bf843c"}, + {file = "wrapt-1.15.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:76407ab327158c510f44ded207e2f76b657303e17cb7a572ffe2f5a8a48aa04d"}, + {file = "wrapt-1.15.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cd525e0e52a5ff16653a3fc9e3dd827981917d34996600bbc34c05d048ca35cc"}, + {file = "wrapt-1.15.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9d37ac69edc5614b90516807de32d08cb8e7b12260a285ee330955604ed9dd29"}, + {file = "wrapt-1.15.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:078e2a1a86544e644a68422f881c48b84fef6d18f8c7a957ffd3f2e0a74a0d4a"}, + {file = "wrapt-1.15.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:2cf56d0e237280baed46f0b5316661da892565ff58309d4d2ed7dba763d984b8"}, + {file = "wrapt-1.15.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:7dc0713bf81287a00516ef43137273b23ee414fe41a3c14be10dd95ed98a2df9"}, + {file = "wrapt-1.15.0-cp39-cp39-win32.whl", hash = "sha256:46ed616d5fb42f98630ed70c3529541408166c22cdfd4540b88d5f21006b0eff"}, + {file = "wrapt-1.15.0-cp39-cp39-win_amd64.whl", hash = "sha256:eef4d64c650f33347c1f9266fa5ae001440b232ad9b98f1f43dfe7a79435c0a6"}, + {file = "wrapt-1.15.0-py3-none-any.whl", hash = "sha256:64b1df0f83706b4ef4cfb4fb0e4c2669100fd7ecacfb59e091fad300d4e04640"}, + {file = "wrapt-1.15.0.tar.gz", hash = "sha256:d06730c6aed78cee4126234cf2d071e01b44b915e725a6cb439a879ec9754a3a"}, +] + +[[package]] +name = "zipp" +version = "3.15.0" +description = "Backport of pathlib-compatible object wrapper for zip files" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "zipp-3.15.0-py3-none-any.whl", hash = "sha256:48904fc76a60e542af151aded95726c1a5c34ed43ab4134b597665c86d7ad556"}, + {file = "zipp-3.15.0.tar.gz", hash = "sha256:112929ad649da941c23de50f356a2b5570c954b65150642bccdd66bf194d224b"}, +] + +[package.extras] +docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +testing = ["big-O", "flake8 (<5)", "jaraco.functools", "jaraco.itertools", "more-itertools", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)"] + +[metadata] +lock-version = "2.0" +python-versions = ">=3.8,<3.11" +content-hash = "32adb8323e0b1fc99dc795fd69857361d48b0aa0cb088cc88f4c3acc0a21a11c" diff --git a/pyeda_test.py b/pyeda_test.py index cc959f1..10f3976 100644 --- a/pyeda_test.py +++ b/pyeda_test.py @@ -10,4 +10,4 @@ f3 = OneHot(a, b, c, d) -f1 = Or(~a & ~b & ~c, ~a & ~b & c, a & ~b & c, a & b & c, a & b & ~c) \ No newline at end of file +f1 = Or(~a & ~b & ~c, ~a & ~b & c, a & ~b & c, a & b & c, a & b & ~c) diff --git a/pylfr.code-workspace b/pylfr.code-workspace new file mode 100644 index 0000000..7e01cbb --- /dev/null +++ b/pylfr.code-workspace @@ -0,0 +1,14 @@ +{ + "folders": [ + { + "path": "." + }, + { + "path": "pymint" + }, + { + "path": "pyparchmint" + } + ], + "settings": {} +} \ No newline at end of file diff --git a/pymint b/pymint new file mode 160000 index 0000000..5d75137 --- /dev/null +++ b/pymint @@ -0,0 +1 @@ +Subproject commit 5d7513757d8129f2c914e4c022c4d4273487f956 diff --git a/pyparchmint b/pyparchmint new file mode 160000 index 0000000..4eec890 --- /dev/null +++ b/pyparchmint @@ -0,0 +1 @@ +Subproject commit 4eec89062f5706df638c363d6616ace3156082ed diff --git a/pyproject.toml b/pyproject.toml index 90b4c2e..4e9c1d2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -13,33 +13,77 @@ include = [ ] [tool.poetry.dependencies] -python = "^3.8" -networkx = "^2.5" +python = ">=3.8,<3.11" +networkx = "^3.0" argparse = "^1.4.0" -antlr4-python3-runtime = "^4.8" +antlr4-python3-runtime = "^4.10" pygraphviz = "^1.6" -parchmint = "latest" -dafd = "latest" numpy = "^1.19.4" tabulate = "^0.8.7" -pymint = "^0.2.11" art = "^5.2" jsonschema = "^3.2.0" pyeda = "^0.28.0" +pymint = {path = "pymint", develop=true} +parchmint = {path = "pyparchmint", develop=true} +scipy = "^1.8.1" +click = "^8.1.3" +dafd = {path = "dafd"} +types-tabulate = "^0.9.0.1" [tool.poetry.dev-dependencies] -mypy = "^0.782" -black = "^20.8b1" flake8 = "^3.8.3" rope = "^0.18.0" +black = "22.3.0" +pytest = "^7.1.2" +isort = "^5.10.1" +mypy = "^1.1.1" +Sphinx = "^3.4.3" +sphinx-rtd-theme = "^0.5.1" +sphinxcontrib-napoleon = "^0.7" +pip = "^23.0.1" [tool.poetry.scripts] lfr-compile = "lfr.cmdline:main" +test = "scripts:test" +format = "scripts:format" +static-analysis = "scripts:static_analysis" [tool.black] -line-length = 88 +include = "lfr/.*\\.py$" +extend-exclude = ''' +# A regex preceded with ^/ will apply only to files and directories +# in the root of the project. +( + ^/lfr/compiler/distribute/BitVector.py # exclude a file named foo.py in the root of the project + | .*_pb2.py # exclude autogenerated Protocol Buffer files anywhere in the project + | ^/lfr/antlrgen/\.* # ANTLR Generated files +) +''' + [build-system] requires = ["poetry-core>=1.0.0"] build-backend = "poetry.core.masonry.api" +[tool.mypy] +python_version = 3.8 +ignore_missing_imports = "True" +check_untyped_defs = "True" +exclude = """ +(?x)( + ^lfr/antlrgen/.*.py$ # files named "one.py" + | BitVector.py$ # or files ending with "two.pyi" + | ^three. # or files starting with "three." +) +""" + +[tool.isort] +profile = "black" +src_paths = ["lfr"] +skip = ["dafd", "lfr/antlrgen/", "lfr/compiler/distribute/BitVector.py"] + +[tool.pytest.ini_options] +minversion = "6.0" +testpaths = [ + "./tests/" +] diff --git a/scripts.py b/scripts.py new file mode 100644 index 0000000..55ae68e --- /dev/null +++ b/scripts.py @@ -0,0 +1,24 @@ +import subprocess + + +def test(): + """ + Run all unittests. Equivalent to: + `poetry run python -u -m unittest discover` + """ + subprocess.run(["pytest", "-vv", "tests"], check=True) + + +def format(): + """ + Runs black and isort + """ + subprocess.run(["isort", "."], check=True) + subprocess.run(["black", "lfr"], check=True) + + +def static_analysis(): + """ + Runs mypy and flake8 + """ + subprocess.run(["mypy", "lfr"], check=True) diff --git a/scripts/all.sh b/scripts/all.sh index 300ee76..1995c6e 100755 --- a/scripts/all.sh +++ b/scripts/all.sh @@ -3,102 +3,102 @@ echo "Synthesizing DropX designs" -for f in ~/CIDAR/LFR-Testcases/dropx/*.lfr; +for f in ./Microfluidics-Benchmarks/LFR-Testcases/dropx/*.lfr; do echo "Running File $f"; - lfr-compile $f --no-gen --outpath ~/CIDAR/LFR-Dry-Run/dropx/ --pre-load ~/CIDAR/LFR-Testcases/distribute-library + lfr-compile $f --no-gen --outpath ./Microfluidics-Benchmarks/LFR-Dry-Run/dropx/ --pre-load ./Microfluidics-Benchmarks/LFR-Testcases/distribute-library done echo "Synthesizing Cassie Thesis Designs" -for f in ~/CIDAR/LFR-Testcases/chthesis/*.lfr; +for f in ./Microfluidics-Benchmarks/LFR-Testcases/chthesis/*.lfr; do echo "Running File $f"; - lfr-compile $f --no-gen --outpath ~/CIDAR/LFR-Dry-Run/chthesis/ --pre-load ~/CIDAR/LFR-Testcases/distribute-library + lfr-compile $f --no-gen --outpath ./Microfluidics-Benchmarks/LFR-Dry-Run/chthesis/ --pre-load ./Microfluidics-Benchmarks/LFR-Testcases/distribute-library done echo "Synthesizing COVID" -for f in ~/CIDAR/LFR-Testcases/COVID/*.lfr; +for f in ./Microfluidics-Benchmarks/LFR-Testcases/COVID/*.lfr; do echo "Running File $f"; - lfr-compile $f --no-gen --outpath ~/CIDAR/LFR-Dry-Run/COVID/ --pre-load ~/CIDAR/LFR-Testcases/distribute-library + lfr-compile $f --no-gen --outpath ./Microfluidics-Benchmarks/LFR-Dry-Run/COVID/ --pre-load ./Microfluidics-Benchmarks/LFR-Testcases/distribute-library done echo "Synthesizing Expressions" -for f in ~/CIDAR/LFR-Testcases/Expressions/*.lfr; +for f in ./Microfluidics-Benchmarks/LFR-Testcases/Expressions/*.lfr; do echo "Running File $f"; - lfr-compile $f --no-gen --outpath ~/CIDAR/LFR-Dry-Run/Expressions/ --pre-load ~/CIDAR/LFR-Testcases/distribute-library + lfr-compile $f --no-gen --outpath ./Microfluidics-Benchmarks/LFR-Dry-Run/Expressions/ --pre-load ./Microfluidics-Benchmarks/LFR-Testcases/distribute-library done echo "Synthesizing ghissues" -for f in ~/CIDAR/LFR-Testcases/ghissues/*.lfr; +for f in ./Microfluidics-Benchmarks/LFR-Testcases/ghissues/*.lfr; do echo "Running File $f"; - lfr-compile $f --no-gen --outpath ~/CIDAR/LFR-Dry-Run/ghissues/ --pre-load ~/CIDAR/LFR-Testcases/distribute-library + lfr-compile $f --no-gen --outpath ./Microfluidics-Benchmarks/LFR-Dry-Run/ghissues/ --pre-load ./Microfluidics-Benchmarks/LFR-Testcases/distribute-library done echo "Synthesizing Graph Coverage" -for f in ~/CIDAR/LFR-Testcases/GraphCoverage/*.lfr; +for f in ./Microfluidics-Benchmarks/LFR-Testcases/GraphCoverage/*.lfr; do echo "Running File $f"; - lfr-compile $f --no-gen --outpath ~/CIDAR/LFR-Dry-Run/GraphCoverage/ --pre-load ~/CIDAR/LFR-Testcases/distribute-library + lfr-compile $f --no-gen --outpath ./Microfluidics-Benchmarks/LFR-Dry-Run/GraphCoverage/ --pre-load ./Microfluidics-Benchmarks/LFR-Testcases/distribute-library done echo "Synthesizing Parser Test" -for f in ~/CIDAR/LFR-Testcases/ParserTest/*.lfr; +for f in ./Microfluidics-Benchmarks/LFR-Testcases/ParserTest/*.lfr; do echo "Running File $f"; - lfr-compile $f --no-gen --outpath ~/CIDAR/LFR-Dry-Run/ParserTest/ --pre-load ~/CIDAR/LFR-Testcases/distribute-library + lfr-compile $f --no-gen --outpath ./Microfluidics-Benchmarks/LFR-Dry-Run/ParserTest/ --pre-load ./Microfluidics-Benchmarks/LFR-Testcases/distribute-library done echo "Synthesizing Protocols" -for f in ~/CIDAR/LFR-Testcases/Protocols/*.lfr; +for f in ./Microfluidics-Benchmarks/LFR-Testcases/Protocols/*.lfr; do echo "Running File $f"; - lfr-compile $f --no-gen --outpath ~/CIDAR/LFR-Dry-Run/Protocols/ --pre-load ~/CIDAR/LFR-Testcases/distribute-library + lfr-compile $f --no-gen --outpath ./Microfluidics-Benchmarks/LFR-Dry-Run/Protocols/ --pre-load ./Microfluidics-Benchmarks/LFR-Testcases/distribute-library done echo "Synthesizing Ryuichi's Designs" -for f in ~/CIDAR/LFR-Testcases/Ryuichi\'s\ designs/*.lfr; +for f in ./Microfluidics-Benchmarks/LFR-Testcases/Ryuichi\'s\ designs/*.lfr; do echo "Running File $f"; - lfr-compile $f --no-gen --outpath ~/CIDAR/LFR-Dry-Run/Ryuichi\'s\ designs/ --pre-load ~/CIDAR/LFR-Testcases/distribute-library + lfr-compile $f --no-gen --outpath ./Microfluidics-Benchmarks/LFR-Dry-Run/Ryuichi\'s\ designs/ --pre-load ./Microfluidics-Benchmarks/LFR-Testcases/distribute-library done echo "Synthesizing Technology Mapping" -for f in ~/CIDAR/LFR-Testcases/TechnologyMapping/*.lfr; +for f in ./Microfluidics-Benchmarks/LFR-Testcases/TechnologyMapping/*.lfr; do echo "Running File $f"; - lfr-compile $f --no-gen --outpath ~/CIDAR/LFR-Dry-Run/TechnologyMapping/ --pre-load ~/CIDAR/LFR-Testcases/distribute-library + lfr-compile $f --no-gen --outpath ./Microfluidics-Benchmarks/LFR-Dry-Run/TechnologyMapping/ --pre-load ./Microfluidics-Benchmarks/LFR-Testcases/distribute-library done diff --git a/scripts/dropx.sh b/scripts/dropx.sh index 7e26582..66009cb 100755 --- a/scripts/dropx.sh +++ b/scripts/dropx.sh @@ -3,9 +3,9 @@ echo "Synthesizing DropX designs" -for f in ~/CIDAR/LFR-Testcases/dropx/*.lfr; +for f in ./Microfluidics-Benchmarks/LFR-Testcases/dropx/*.lfr; do echo "Running File $f"; - lfr-compile $f --outpath ~/CIDAR/MINT-TestCases/dropx/ + lfr-compile $f --outpath ./Microfluidics-Benchmarks/MINT-TestCases/dropx/ done \ No newline at end of file diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/conftest.py b/tests/conftest.py new file mode 100644 index 0000000..4755b12 --- /dev/null +++ b/tests/conftest.py @@ -0,0 +1,15 @@ +from pathlib import Path + +import networkx + +from lfr.fig.fluidinteractiongraph import FluidInteractionGraph + +TEST_OUTPATH = Path(__file__).parent.joinpath("out").absolute() +TEST_DATA_FOLDER = Path(__file__).parent.joinpath("data") +LIBRARY_PATH = Path(__file__).parent.parent.joinpath("library").absolute() + + +def to_agraph(fig): + fig_copy = fig.copy(as_view=False) + ret = networkx.nx_agraph.to_agraph(fig_copy) + return ret diff --git a/tests/data/DropX/dx1.lfr b/tests/data/DropX/dx1.lfr new file mode 100644 index 0000000..f00e721 --- /dev/null +++ b/tests/data/DropX/dx1.lfr @@ -0,0 +1,21 @@ +module dx1( + finput in1, in2, + foutput out1, out2 +); + + flow base_droplets, injected_droplets, incubated_droplets, sorted_droplets, discarded_droplets; + + assign base_droplets = in1%100; + + assign injected_droplets = base_droplets + in2; + + #MAP "MIXER" "~" + assign incubated_droplets = ~injected_droplets; + + assign sorted_droplets = incubated_droplets - discarded_droplets; + + assign out1 = sorted_droplets; + assign out2 = discarded_droplets; + + +endmodule \ No newline at end of file diff --git a/tests/data/DropX/dx10.lfr b/tests/data/DropX/dx10.lfr new file mode 100644 index 0000000..e903814 --- /dev/null +++ b/tests/data/DropX/dx10.lfr @@ -0,0 +1,11 @@ +// Based on the design by Samuel Oliveria + +module dx10( + finput in1, in2, + foutput out1, out2 +); + flow temp; + assign temp = in1 % 100 + in2; + assign out1 = temp - out2; + +endmodule \ No newline at end of file diff --git a/tests/data/DropX/dx11.lfr b/tests/data/DropX/dx11.lfr new file mode 100644 index 0000000..34f38a4 --- /dev/null +++ b/tests/data/DropX/dx11.lfr @@ -0,0 +1,25 @@ +// Based on the design by Eric South + +module dx11( + finput in1, in2, in3, + foutput waste1, waste2, final_winners +); + flow injected_droplets; + + assign injected_droplets = in1 % 100 + in2; + + flow sort_candidates1, sort_candidates2, temp_winners; + + #MAP "MIXER" "~" + assign sort_candidates1 = ~(injected_droplets); + + assign temp_winners = sort_candidates1 - waste1; + + + #MAP "MIXER" "~" + assign sort_candidates2 = ~(in3 + temp_winners); + + assign final_winners = sort_candidates2 - waste2; + + +endmodule \ No newline at end of file diff --git a/tests/data/DropX/dx12.lfr b/tests/data/DropX/dx12.lfr new file mode 100644 index 0000000..86db299 --- /dev/null +++ b/tests/data/DropX/dx12.lfr @@ -0,0 +1,23 @@ +// Based on the Design by Rita Chen + +module dx12( + finput plasmids, dig1, dig2, + foutput dig_plasmids, waste +); + + flow enc_plasmids, injected_dig, incubated_plasmids, digest_plasmids, discarded_droplets; + + assign enc_plasmids = plasmids%100; + + assign injected_dig = enc_plasmids + dig1 + dig2; + + #MAP "MIXER" "~" + assign incubated_plasmids = ~injected_dig; + + assign digest_plasmids = incubated_plasmids - discarded_droplets; + + assign dig_plasmids = digest_plasmids; + assign waste = discarded_droplets; + + +endmodule diff --git a/tests/data/DropX/dx13.lfr b/tests/data/DropX/dx13.lfr new file mode 100644 index 0000000..5028346 --- /dev/null +++ b/tests/data/DropX/dx13.lfr @@ -0,0 +1,36 @@ +// Based on the design by Diana Arguijo + +module dx13( + finput in1, in2, in3, + foutput out1, out2, out3, out4 +); + + flow droplets_1, mix_1, pico_1, mix_2, sorted_trash_1, sorted_1, pico_2, mix_3, sorted_trash_2, sorted_2, splitter_1, splitter_2; + + assign droplets_1 = in1%100; + + #MAP "MIXER" "~" + assign mix_1 = ~droplets_1; + + assign pico_1 = mix_1 + in2; + + #MAP "MIXER" "~" + assign mix_2 = ~pico_1; + + assign sorted_1 = mix_2 - sorted_trash_1; + + assign pico_2 = sorted_1 + in3; + + #MAP "MIXER" "~" + assign mix_3 = ~pico_2; + + assign sorted_2 = mix_3 - sorted_trash_2; + + assign {splitter_1, splitter_2} = sorted_2/2; + + assign out1 = sorted_trash_1; + assign out2 = sorted_trash_2; + assign out3 = splitter_1; + assign out4 = splitter_2; + +endmodule \ No newline at end of file diff --git a/tests/data/DropX/dx14.lfr b/tests/data/DropX/dx14.lfr new file mode 100644 index 0000000..59b6790 --- /dev/null +++ b/tests/data/DropX/dx14.lfr @@ -0,0 +1,21 @@ +// Based on the design by Ali Lashkaripour + +module dx14( + finput in1, in2, in3, + foutput waste, keep +); + + flow input_mix; + assign input_mix = in1 + in2 + in3; + + flow [0:1] droplet_inputs; + assign droplet_inputs = ~input_mix; + + flow join; + + #MAP "MIXER" "~" + assign join = ~(droplet_inputs%100); + + assign keep = join - waste; + +endmodule \ No newline at end of file diff --git a/tests/data/DropX/dx15.lfr b/tests/data/DropX/dx15.lfr new file mode 100644 index 0000000..3fab413 --- /dev/null +++ b/tests/data/DropX/dx15.lfr @@ -0,0 +1,9 @@ +// Based on the design by Helen Huang + +module dx15( + finput in1, + foutput collection, waste +); + assign collection = ~(in1%100) - waste; + +endmodule \ No newline at end of file diff --git a/tests/data/DropX/dx2.lfr b/tests/data/DropX/dx2.lfr new file mode 100644 index 0000000..b993432 --- /dev/null +++ b/tests/data/DropX/dx2.lfr @@ -0,0 +1,18 @@ +module dx2( + foutput in1, in2, + foutput out1, out2 +); + + flow droplets1, droplets2, merged_droplets, incubated_droplets; + + assign droplets1 = in1%100; + assign droplets2 = in2%200; + + assign merged_droplets = droplets1 + droplets2; + + #MAP "MIXER" "~" + assign incubated_droplets = ~merged_droplets; + + assign out1 = incubated_droplets - out2; + +endmodule \ No newline at end of file diff --git a/tests/data/DropX/dx3.lfr b/tests/data/DropX/dx3.lfr new file mode 100644 index 0000000..1f80eb4 --- /dev/null +++ b/tests/data/DropX/dx3.lfr @@ -0,0 +1,21 @@ +module dx3( + finput in1, in2, in3, + foutput out1, out2 +); + + flow droplets, split1, split2; + + assign droplets = in1%100; + + assign {split1, split2} = droplets/2; + + flow injected_droplets1, injected_droplets2; + + assign injected_droplets1 = split1 + in2; + + assign injected_droplets2 = split2 + in3; + + assign out1 = injected_droplets1; + assign out2 = injected_droplets2; + +endmodule \ No newline at end of file diff --git a/tests/data/DropX/dx4.lfr b/tests/data/DropX/dx4.lfr new file mode 100644 index 0000000..a170fab --- /dev/null +++ b/tests/data/DropX/dx4.lfr @@ -0,0 +1,22 @@ +module dx4( + finput in1, pico_in1, pico_in2, pico_in3, pico_in4, + foutput sort_keep1, sort_keep2, sort_waste1, sort_waste2 +); + + flow pico_mix; + assign pico_mix = pico_in1 + pico_in2 + pico_in3 + pico_in3 + pico_in4; + + flow droplets; + assign droplets = in1%100; + + flow injected_droplets; + assign injected_droplets = droplets + pico_mix; + + flow sort_keep_temp, sort_waste_temp; + + assign sort_keep_temp = injected_droplets - sort_waste_temp; + + assign sort_keep1 = sort_keep_temp - sort_waste1; + assign sort_keep2 = sort_waste_temp - sort_waste2; + +endmodule \ No newline at end of file diff --git a/tests/data/DropX/dx5.lfr b/tests/data/DropX/dx5.lfr new file mode 100644 index 0000000..b35435a --- /dev/null +++ b/tests/data/DropX/dx5.lfr @@ -0,0 +1,18 @@ +module dx5 ( + finput in1, in2, + foutput sort_keep1, sort_keep2, sort_waste1, sort_waste2 +); + + flow injected_droplets; + assign injected_droplets = in2 + in1%100; + + flow split1, split2; + assign {split1, split2} = injected_droplets/2; + + #MAP "MIXER" "~" + assign sort_keep1 = ~split1 - sort_waste1; + + #MAP "MIXER" "~" + assign sort_keep2 = ~split2 - sort_waste2; + +endmodule \ No newline at end of file diff --git a/tests/data/DropX/dx6.lfr b/tests/data/DropX/dx6.lfr new file mode 100644 index 0000000..f92ecf7 --- /dev/null +++ b/tests/data/DropX/dx6.lfr @@ -0,0 +1,18 @@ +module dx6( + finput in1, in2, + foutput out1, out2 +); + + flow mix; + assign mix = in1 + in2; + + flow droplets; + assign droplets = mix%50; + + flow split1, split2; + assign {split1, split2} = droplets/2; + + assign out1 = split1; + assign out2 = split2; + +endmodule \ No newline at end of file diff --git a/tests/data/DropX/dx7.lfr b/tests/data/DropX/dx7.lfr new file mode 100644 index 0000000..5507208 --- /dev/null +++ b/tests/data/DropX/dx7.lfr @@ -0,0 +1,13 @@ +module dx7( + finput in1, in2, pico_in1, pico_in2, + foutput out1, out2 +); + + flow merged_droplets, injected_droplets; + assign merged_droplets = in1%100 + in2%100; + + assign injected_droplets = (pico_in1 + pico_in2) + merged_droplets; + + assign {out1, out2} = injected_droplets/2; + +endmodule \ No newline at end of file diff --git a/tests/data/DropX/dx8.lfr b/tests/data/DropX/dx8.lfr new file mode 100644 index 0000000..6770a99 --- /dev/null +++ b/tests/data/DropX/dx8.lfr @@ -0,0 +1,25 @@ +// Based on the design by Leslie Huang + +module dx8( + finput in1, in2, in3, in4, in5, in6, + foutput out1, out2, out3 +); + + flow in_mix; + assign in_mix = in1 + in2 + in3; + + flow droplets; + assign droplets = in_mix % 100; + + flow injected_droplets; + assign injected_droplets = (in6 + (in5 + (in4 + droplets))); + + flow mixed_droplets; + + #MAP "MIXER" "~" + assign mixed_droplets = ~injected_droplets; + flow temp; + assign temp = mixed_droplets - out2; + assign out3 = temp - out1; + +endmodule \ No newline at end of file diff --git a/tests/data/DropX/dx9.lfr b/tests/data/DropX/dx9.lfr new file mode 100644 index 0000000..761e8e3 --- /dev/null +++ b/tests/data/DropX/dx9.lfr @@ -0,0 +1,17 @@ +// Based on the design by David McIntyre + +module dx9( + finput in1, in2, in3, + foutput out1, out2 +); + + + flow merged_droplets; + assign merged_droplets = in1 % 100 + in2 % 50; + + flow injected_droplets; + assign injected_droplets = merged_droplets + in3; + + assign out2 = injected_droplets - out1; + +endmodule \ No newline at end of file diff --git a/tests/data/DropX/figs/dx1.dot b/tests/data/DropX/figs/dx1.dot new file mode 100644 index 0000000..df453bd --- /dev/null +++ b/tests/data/DropX/figs/dx1.dot @@ -0,0 +1,15 @@ +strict digraph "" { + in1 -> "in1_METER_(%)_60"; + in2 -> "base_droplets_MIX_(+)_in2_61"; + base_droplets -> "base_droplets_MIX_(+)_in2_61"; + injected_droplets -> "injected_droplets_PROCESS_(~)_62"; + incubated_droplets -> "discarded_droplets_SIEVE_(-)_incubated_droplets_63"; + sorted_droplets -> out1; + discarded_droplets -> out2; + "in1_METER_(%)_60" -> base_droplets; + val_1 -> "in1_METER_(%)_60"; + "base_droplets_MIX_(+)_in2_61" -> injected_droplets; + "injected_droplets_PROCESS_(~)_62" -> incubated_droplets; + "discarded_droplets_SIEVE_(-)_incubated_droplets_63" -> sorted_droplets; + "discarded_droplets_SIEVE_(-)_incubated_droplets_63" -> discarded_droplets; +} diff --git a/tests/data/DropX/figs/dx1.dot.pdf b/tests/data/DropX/figs/dx1.dot.pdf new file mode 100644 index 0000000000000000000000000000000000000000..d8c9cabbb5bf296b01b8b46927bdc9c674dd6a6c GIT binary patch literal 12307 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 aCH~-CW^lys=z>6kJU}dFW?3~ktp5*tz!}#7 literal 0 HcmV?d00001 diff --git a/tests/data/DropX/figs/dx10.dot b/tests/data/DropX/figs/dx10.dot new file mode 100644 index 0000000..ffc47b4 --- /dev/null +++ b/tests/data/DropX/figs/dx10.dot @@ -0,0 +1,10 @@ +strict digraph "" { + in1 -> "in1_METER_(%)_112"; + in2 -> "in1_METER_(%)_112_MIX_(+)_in2_113"; + temp -> "out2_SIEVE_(-)_temp_114"; + "in1_METER_(%)_112" -> "in1_METER_(%)_112_MIX_(+)_in2_113"; + val_1 -> "in1_METER_(%)_112"; + "in1_METER_(%)_112_MIX_(+)_in2_113" -> temp; + "out2_SIEVE_(-)_temp_114" -> out1; + "out2_SIEVE_(-)_temp_114" -> out2; +} diff --git a/tests/data/DropX/figs/dx10.dot.pdf b/tests/data/DropX/figs/dx10.dot.pdf new file mode 100644 index 0000000000000000000000000000000000000000..f8949040904b0aefa1e3e6d03571bd1159593bff GIT binary patch literal 10169 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 literal 0 HcmV?d00001 diff --git a/tests/data/DropX/figs/dx11.dot b/tests/data/DropX/figs/dx11.dot new file mode 100644 index 0000000..cd157e3 --- /dev/null +++ b/tests/data/DropX/figs/dx11.dot @@ -0,0 +1,19 @@ +strict digraph "" { + in1 -> "in1_METER_(%)_115"; + in2 -> "in1_METER_(%)_115_MIX_(+)_in2_116"; + in3 -> "in3_MIX_(+)_temp_winners_119"; + injected_droplets -> "injected_droplets_PROCESS_(~)_117"; + "in1_METER_(%)_115" -> "in1_METER_(%)_115_MIX_(+)_in2_116"; + val_1 -> "in1_METER_(%)_115"; + "in1_METER_(%)_115_MIX_(+)_in2_116" -> injected_droplets; + sort_candidates1 -> "sort_candidates1_SIEVE_(-)_waste1_118"; + sort_candidates2 -> "sort_candidates2_SIEVE_(-)_waste2_121"; + temp_winners -> "in3_MIX_(+)_temp_winners_119"; + "injected_droplets_PROCESS_(~)_117" -> sort_candidates1; + "sort_candidates1_SIEVE_(-)_waste1_118" -> waste1; + "sort_candidates1_SIEVE_(-)_waste1_118" -> temp_winners; + "in3_MIX_(+)_temp_winners_119" -> "in3_MIX_(+)_temp_winners_119_PROCESS_(~)_120"; + "in3_MIX_(+)_temp_winners_119_PROCESS_(~)_120" -> sort_candidates2; + "sort_candidates2_SIEVE_(-)_waste2_121" -> waste2; + "sort_candidates2_SIEVE_(-)_waste2_121" -> final_winners; +} diff --git a/tests/data/DropX/figs/dx11.dot.pdf b/tests/data/DropX/figs/dx11.dot.pdf new file mode 100644 index 0000000000000000000000000000000000000000..27fe0eb89ebf4d16f0178c09f845d3bfc2d9c94d GIT binary patch literal 17165 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 literal 0 HcmV?d00001 diff --git a/tests/data/DropX/figs/dx12.dot b/tests/data/DropX/figs/dx12.dot new file mode 100644 index 0000000..2102638 --- /dev/null +++ b/tests/data/DropX/figs/dx12.dot @@ -0,0 +1,17 @@ +strict digraph "" { + plasmids -> "plasmids_METER_(%)_122"; + dig1 -> "dig1_MIX_(+)_enc_plasmids_123"; + dig2 -> "dig1_MIX_(+)_enc_plasmids_123_MIX_(+)_dig2_124"; + enc_plasmids -> "dig1_MIX_(+)_enc_plasmids_123"; + injected_dig -> "injected_dig_PROCESS_(~)_125"; + incubated_plasmids -> "discarded_droplets_SIEVE_(-)_incubated_plasmids_126"; + digest_plasmids -> dig_plasmids; + discarded_droplets -> waste; + "plasmids_METER_(%)_122" -> enc_plasmids; + val_1 -> "plasmids_METER_(%)_122"; + "dig1_MIX_(+)_enc_plasmids_123" -> "dig1_MIX_(+)_enc_plasmids_123_MIX_(+)_dig2_124"; + "dig1_MIX_(+)_enc_plasmids_123_MIX_(+)_dig2_124" -> injected_dig; + "injected_dig_PROCESS_(~)_125" -> incubated_plasmids; + "discarded_droplets_SIEVE_(-)_incubated_plasmids_126" -> digest_plasmids; + "discarded_droplets_SIEVE_(-)_incubated_plasmids_126" -> discarded_droplets; +} diff --git a/tests/data/DropX/figs/dx12.dot.pdf b/tests/data/DropX/figs/dx12.dot.pdf new file mode 100644 index 0000000000000000000000000000000000000000..6400deb8f17487ec17630a95f0d030518bb7603b GIT binary patch literal 13733 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 "in1_METER_(%)_127"; + in2 -> "in2_MIX_(+)_mix_1_129"; + in3 -> "in3_MIX_(+)_sorted_1_132"; + droplets_1 -> "droplets_1_PROCESS_(~)_128"; + mix_1 -> "in2_MIX_(+)_mix_1_129"; + pico_1 -> "pico_1_PROCESS_(~)_130"; + mix_2 -> "mix_2_SIEVE_(-)_sorted_trash_1_131"; + sorted_trash_1 -> out1; + sorted_1 -> "in3_MIX_(+)_sorted_1_132"; + pico_2 -> "pico_2_PROCESS_(~)_133"; + mix_3 -> "mix_3_SIEVE_(-)_sorted_trash_2_134"; + sorted_trash_2 -> out2; + sorted_2 -> "sorted_2_DIVIDE_(/)_135"; + splitter_1 -> out3; + splitter_2 -> out4; + "in1_METER_(%)_127" -> droplets_1; + val_1 -> "in1_METER_(%)_127"; + "droplets_1_PROCESS_(~)_128" -> mix_1; + "in2_MIX_(+)_mix_1_129" -> pico_1; + "pico_1_PROCESS_(~)_130" -> mix_2; + "mix_2_SIEVE_(-)_sorted_trash_1_131" -> sorted_trash_1; + "mix_2_SIEVE_(-)_sorted_trash_1_131" -> sorted_1; + "in3_MIX_(+)_sorted_1_132" -> pico_2; + "pico_2_PROCESS_(~)_133" -> mix_3; + "mix_3_SIEVE_(-)_sorted_trash_2_134" -> sorted_trash_2; + "mix_3_SIEVE_(-)_sorted_trash_2_134" -> sorted_2; + "sorted_2_DIVIDE_(/)_135" -> splitter_1; + "sorted_2_DIVIDE_(/)_135" -> splitter_2; + val_2 -> "sorted_2_DIVIDE_(/)_135"; +} diff --git a/tests/data/DropX/figs/dx13.dot.pdf b/tests/data/DropX/figs/dx13.dot.pdf new file mode 100644 index 0000000000000000000000000000000000000000..ac88b39e0b336a0ed54a3a99dbba3073ba494b1a GIT binary patch literal 15551 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 literal 0 HcmV?d00001 diff --git a/tests/data/DropX/figs/dx14.dot b/tests/data/DropX/figs/dx14.dot new file mode 100644 index 0000000..a020bc5 --- /dev/null +++ b/tests/data/DropX/figs/dx14.dot @@ -0,0 +1,21 @@ +strict digraph "" { + in1 -> "in1_MIX_(+)_in2_136"; + in2 -> "in1_MIX_(+)_in2_136"; + in3 -> "in1_MIX_(+)_in2_136_MIX_(+)_in3_137"; + input_mix -> "input_mix_PROCESS_(~)_138"; + "in1_MIX_(+)_in2_136" -> "in1_MIX_(+)_in2_136_MIX_(+)_in3_137"; + "in1_MIX_(+)_in2_136_MIX_(+)_in3_137" -> input_mix; + droplet_inputs_0 -> "droplet_inputs_0_METER_(%)_139"; + droplet_inputs_1 -> "droplet_inputs_1_METER_(%)_140"; + "input_mix_PROCESS_(~)_138" -> droplet_inputs_0; + "input_mix_PROCESS_(~)_138" -> droplet_inputs_1; + join -> "join_SIEVE_(-)_waste_143"; + "droplet_inputs_0_METER_(%)_139" -> "droplet_inputs_0_METER_(%)_139_PROCESS_(~)_141"; + val_1 -> "droplet_inputs_0_METER_(%)_139"; + "droplet_inputs_1_METER_(%)_140" -> "droplet_inputs_1_METER_(%)_140_PROCESS_(~)_142"; + val_2 -> "droplet_inputs_1_METER_(%)_140"; + "droplet_inputs_0_METER_(%)_139_PROCESS_(~)_141" -> join; + "droplet_inputs_1_METER_(%)_140_PROCESS_(~)_142" -> join; + "join_SIEVE_(-)_waste_143" -> waste; + "join_SIEVE_(-)_waste_143" -> keep; +} diff --git a/tests/data/DropX/figs/dx14.dot.pdf b/tests/data/DropX/figs/dx14.dot.pdf new file mode 100644 index 0000000000000000000000000000000000000000..d5e41c4c4e311f709e9733247dbe0f9866c29233 GIT binary patch literal 14899 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 literal 0 HcmV?d00001 diff --git a/tests/data/DropX/figs/dx15.dot b/tests/data/DropX/figs/dx15.dot new file mode 100644 index 0000000..c9d5e03 --- /dev/null +++ b/tests/data/DropX/figs/dx15.dot @@ -0,0 +1,8 @@ +strict digraph "" { + in1 -> "in1_METER_(%)_144"; + "in1_METER_(%)_144" -> "in1_METER_(%)_144_PROCESS_(~)_145"; + val_1 -> "in1_METER_(%)_144"; + "in1_METER_(%)_144_PROCESS_(~)_145" -> "in1_METER_(%)_144_PROCESS_(~)_145_SIEVE_(-)_waste_146"; + "in1_METER_(%)_144_PROCESS_(~)_145_SIEVE_(-)_waste_146" -> collection; + "in1_METER_(%)_144_PROCESS_(~)_145_SIEVE_(-)_waste_146" -> waste; +} diff --git a/tests/data/DropX/figs/dx15.dot.pdf b/tests/data/DropX/figs/dx15.dot.pdf new file mode 100644 index 0000000000000000000000000000000000000000..cf296ea40dbee1858ae06765bffef22379e1d5c3 GIT binary patch literal 10306 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 Av;Y7A literal 0 HcmV?d00001 diff --git a/tests/data/DropX/figs/dx2.dot b/tests/data/DropX/figs/dx2.dot new file mode 100644 index 0000000..0788770 --- /dev/null +++ b/tests/data/DropX/figs/dx2.dot @@ -0,0 +1,16 @@ +strict digraph "" { + in1 -> "in1_METER_(%)_64"; + in2 -> "in2_METER_(%)_65"; + droplets1 -> "droplets1_MIX_(+)_droplets2_66"; + droplets2 -> "droplets1_MIX_(+)_droplets2_66"; + merged_droplets -> "merged_droplets_PROCESS_(~)_67"; + incubated_droplets -> "incubated_droplets_SIEVE_(-)_out2_68"; + "in1_METER_(%)_64" -> droplets1; + val_1 -> "in1_METER_(%)_64"; + "in2_METER_(%)_65" -> droplets2; + val_2 -> "in2_METER_(%)_65"; + "droplets1_MIX_(+)_droplets2_66" -> merged_droplets; + "merged_droplets_PROCESS_(~)_67" -> incubated_droplets; + "incubated_droplets_SIEVE_(-)_out2_68" -> out1; + "incubated_droplets_SIEVE_(-)_out2_68" -> out2; +} diff --git a/tests/data/DropX/figs/dx2.dot.pdf b/tests/data/DropX/figs/dx2.dot.pdf new file mode 100644 index 0000000000000000000000000000000000000000..6b475b06e134c8077955ed49228872126c2d914c GIT binary patch literal 13040 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 "in1_METER_(%)_69"; + in2 -> "in2_MIX_(+)_split1_71"; + in3 -> "in3_MIX_(+)_split2_72"; + droplets -> "droplets_DIVIDE_(/)_70"; + split1 -> "in2_MIX_(+)_split1_71"; + split2 -> "in3_MIX_(+)_split2_72"; + "in1_METER_(%)_69" -> droplets; + val_1 -> "in1_METER_(%)_69"; + "droplets_DIVIDE_(/)_70" -> split1; + "droplets_DIVIDE_(/)_70" -> split2; + val_2 -> "droplets_DIVIDE_(/)_70"; + injected_droplets1 -> out1; + injected_droplets2 -> out2; + "in2_MIX_(+)_split1_71" -> injected_droplets1; + "in3_MIX_(+)_split2_72" -> injected_droplets2; +} diff --git a/tests/data/DropX/figs/dx3.dot.pdf b/tests/data/DropX/figs/dx3.dot.pdf new file mode 100644 index 0000000000000000000000000000000000000000..50a0e75e47edeb2e0e8af285c05233bf6fcea522 GIT binary patch literal 11892 zcma*N1z1(v6F01cq=X@Y{b>&l+qN41W zM>EKos$X}D;Mw4a_k|M$v6?v1(_FfUO8u7lR?ck26_ihoVYNSy;q*F0Rg~Uug!mF>m6`vB`-jq3CoNRJdx>h<=x;9)ss~EhF z-@Kgl(yG9_*({y&JTmRd3sIk)KPbDLuYBX5e?`01Bdl_c$JXPXnJ0jnS-le0%J z`T&oo6gQqUF0f&AbGUSQUUpu^vqmp=^!v5N#4+x2v&*^^t`SuM@4$H#-MP62CN%Ot#NlMph6kY7C2=lRL94nP%s6G%;8q z@tC(~l{Z(Q+6KV;7LN^$15#YprpxB9;8=&7ONGwn%T%6pUKK=_z0}ilplwznorc3d zsJpeR9(B^vc`o~|3tO)U&K`T6-+aO+3QHTF-u20GjzzLtm!;6GR$f5PhUc~;xQlCZ zJui1{fONelYtuTw*Q9%JGR{%pw5ymdrttmU%mD?7E6>Ze*13?EB(8=B7gug@r_SeA zDC_8tMJyL<$(c~dH;*D8vy5>XAlMbG5M`FbIo_zen5sN|uA^}?VTmw;#;+6q!o@3= zaFk+!7h!DFkxsv1MOOS8{ZTd~s?NGVDLm%{apIVrMl9xlFxU6pRAem8aaBemQ3ZN= zQpSfRNquSj_qEs~FbTVA3qn_1m#72|k!O`ITm_Wl}T zscrU57dHrP8~gk=(|$_tUdM+>w*@~UGHa`Zke zkrow~a~rM*f9N2|INEU9ed65*4qyw|o!_3af)y zOik*tVYp&Sjwg+|lCCtr1@@4cLV110*&O=C6+Pq{lOdv?;Rr0elPE3xT@_WLGZsB} zIW*3u78@M4)pU?&UtwuV1o#qFI%n0XwMn6`F{7{=$$KH{wV2v#Etb`b6`)|))L@jD zgC=Q3%W(5l#Zb}~T~~(RVx!2LCAXsT*tH`zY4q+BXVFztx{Rj6B)d9vWI3UXr#SJL zt7y1!--F-$-6lWl;~!*@L&b%{@6@uzaI|o!<-Cte6fh1U!?d!8>#6F8$)@WSvGJ^( znOO`JxF6N>a~TF?B7m|2f9xS2dV#P-DtQr953l6z&`g(dI43|Zv9T0Uc|?kg;JC5B zx_*@W3Ki#bZEv=_j}lV;#J;(fe6#RHcqmEYR(--+z~cz9koSZ!>u{9^P0#(d?FQzB z3Jxl;UGyV_f~wS{ozoHm9=pDpmW1zbduoQ&QKjspWp$1>)O6x(5>6{tI8r6JlFwsf zKae$@%vBXwC+H|$(CeIw7Jn%{+-9819#EDncT$xs*BAYzqV$;RJM%)aGvhax^NYB* zOQcuAE(LsI6>{qxIo3;w2z_PkKij?#@^gYHW3 zat!Q5bf8GxJ{9cN(|^bMmY_wsjL$Wv!#eqcRXqD<;vjl|svg@8O>-T*tdOX=)2nZ?_gh%XYItS3L3 zsE-*RPoc_3=>=9YO+yG{o%Kb_+nYYW+^SXT6#`#3Y2)oC5S2lReffa?iim{+Ztowpk2(2f)jzlq%*Sezlid*qlVi@2^(QD zh~TD=%1Y_zB*cPzk^%`ziQUlM7d3*H7Y}a4b-e;@+V2j=pBHmQCM^@pDQX|2=b)%7 zc*n=p=)K)+`X;}0RDLXxCBa*Y?mSQ>M`moJ1b&^w7o?lEB18GaD45qya4lT89v^l`b&UhOfSIROg!ZtAycWG^U zP?aMUf)iEvvrimF^S(NAQEs1gu5vPHcPL8syPdp0y4*(W8%Ha7Eod!v>NiX{k!58oUy#ny$V+vF+lp2~-Z&9) zGb&8wx8|27fr?z?*g8Zc(HxfJHcw#lqC)}yxLZc?l{81chir#J$QF0}uRTv?$abJ- zT{nv^OJXi+&8}|EenA_YmXqMnyG4*4WM~z#lrg(>*s;eYcpxEvr%1n&B2fp}YbDryLImLBdtlrjY`VaE0>CCL2mk@t^mOm9 z_i567$L}kfn56~mAJT>_Ko%f?^G}*}FLeL+moo6*$^dN2j#dT$HhEnm0{{&G^h>$C zu8qO{yFh=&vdI|eLv@8L9RQm5EFge`gA>3B1_Ivxk`}(#doOMac*yd8r*yAvEbXlR zF_+&uesTX8qjay?M%TjD>Oq&D;~%yJ0A^!n@P`tH?V-7(%&pS(Gz^d-me$Kn%uYjV(Clhy|ALO2qQc92L)flr8S6$ zw)@p$K;&=k}cjY?X1wS7f=At*ocf=hrJtNMXmUt>AN^4wGH zoi`=UOYeVNw^r}pf`u9iGYl}>Aw*?wkr5TYSe`4c7b7t@em*5JHe!AI%~04jZ=a@& z>y^{op}#05#q~@CG9sceSD}M(tK>(B(xaWUL5zIobhDMopl!@SZ~izW?9X_nPmkZP z(#!Wyq%aQdQJ%i-g|y=a9Zs>@p^FY@1WS=B_>e@l3i4FNit*=3NDioJz#G*c2$1WR z>IUUBX`&p_lmHNA6lU7=i(q8oUXo0d`LT2&Bo%x-o8dK+di*<=;TNRel7pqlMdik- zL9E|L;-h$u=0pk`j*zMFaZfND+epuNyq~i9R^!qUcX!(0(l@uj@hB5Gf6%%Owbna2 z>bTj7nr-CdTFU6nrC92~>>_`0x|%BD*~{p|;Z9Z)K^8JRC~1dG$Lqr0yvt&!*hM1w zO%tV%eJ?W9IfjZOqkaxn>(91@hu#~6ZW{aV!8!|AWV(X;&+J-K}*+ck1G>J+?=$Z44RBX%K^{v$^lYyj-OP=a9=%6 zwCv^@;T6Es3vLb(O)>37!Fytm3Dg20Jtt`?J4JbS%btmIyysS5omN|tBAcEuB(C|G zprqcr;(aRp9Thyc0{S)ldmXFQ-sS0bWa~Xh3MHm}b_9ua@Y=R@@*1w+%A2e<>up%O z?wYX~?Q=e*&2R)yrhH$OM2z4nN6pOber0|2UUmbGb?J6*a89b4K-sC(&JOA2m*-u( zlulvRu-!L5vik|jo?43sQiSLmykgBVd+DB1Gt+ippD@$oxb1ocaBAS63frk`y~S|~ zOY7wMoM6`2@p23_#*>m@sco!1|7Kq6J4?CR&lsL)o*%Yj_5wX{8+_LLviNJuNx{sl z5d${*JU+IF&R^07dBPH!LOw{t`d3!>ZAontty%j!NCOg0tIDs5I!ug$_KNqs_7d4j z37(&|c6fb~%qHgp*gI(2ow{5zNv0x9pYK-BMwb%hjtx#AeCoD*_l0VOOMy~Q!1_oV zda8%$74f7V?!gf<)%A|n#W_=XJl-jkviE;8s8BOs2B|lXXSo0TG&!i ziBI6w%)rU6zkDHMJeEE_TQdhO+AQPosQC6O;i*av1MPPq#wtZ@H9%=lmU%u$1ipS` zm*TUgn);23eA)V_G<`rC zZZ@1Prqj+Q!dceP-_FwxL^nY%zh7ZH8OdlU9>uw$<+)oEELE1d#f1pJ6v--Qk1&|% z2_CYyD`_{Qgk!^wf0S$tPRdeX{G!CfoFE*GnxTxhflA!tC=A6*#&`quc6dv#V?bEM zp@jUVUX}r(61vm8?_T$~dTpTRlydN`Q<#P6GPRf9H*#J*jjVccHi4Gd3q1L!@kzlm zHBf3{E7oYs3Gr4F?@!+rKI=GoE|2&bd@8KN=nd3VzwD6v7?R8{b#t}Xcw=Bm6HM~6 z{>t6{^JTa79kaO*oVLqlR(%oevGCPS^(VU0O(G)ujWs)w+E-&GveaLdRhC8C3QI~l znQ^)q?M})bOQ$D?`1^n54hW~YLjF8PZjpO>Ma@;{3^t2H^0b%zhM=i>OW=PS{1gkiQS3SkT20mZuy^-q+8FLGRY$ zWX}IdN&6nh(?9h6GaDse;3ya!0cBTm^BSF*0~srwuKd$wCE;7ewVki<)gCa^A*cH?$an5e4`zzd5wWAz_MP^&TCwawB@G3T9$ANpCE3R5^TE|Z~ncdCeYyYg=`#d`saCO$O-!kjc_{OTB{A6=%9HN%Pc6-au^v>Mn=uB-O zvB7X1%HL@pK{17^G7nr-Q)dDD7&EyGjDr9guNSOpnH9o>Vs5hqs?4XF3yHbP&@xc2 zxm_RW+&O%Oea0KYfDV{o!xzb&xJYmj*gQ=WHSu=;2_|M#y6zd_^6plQW}s1jY+Dmh zY%i)K6W9@s2#27uH(ZHu_ag$=KtcIw0zw4+$TO;M&*+XYnkghzyJ#g!5?S;EAlDE$ zi0A2|)iZN%px^FI=%UCMbZRN&{dH9#VJrGiUz799&GHLOjh*$bdwTAC{p@c(7M+rk z1+<>k+1xQM`1#E7zBpLaw0!Hl*Y`mnts#L^W$SWPYDfD(>>4l|tSupT_l5b(&FuW1 z^?VWD`4OC3y9w}Oz}U@tP2 z+9xU3s8B)Tvw^O2S-A&4^29R0qmu10jx4UoS++W>h9dIeF3!yrclGc$R23VVMcMA; zEOBbjBYEB2fy9W%Yd^+jAtsSz<*+hhB;?-e$9%acpVQ^-vUx<77K<%tV8Pify7s;b z(>2Y2%3zGxFKF@Q-xt`F%WrPJPpPn>yCJe!Ow`({x!e>cIpO%}9ej2W`M^ci0F*VL zvv952o<`EXUYJao<0?kC#=k&VWW}5;GTf&{`V>~6C>Cg%WtZ~J@v1^B~_UMZyGd;g>-*jQQFs*RS!{saG^!HJUev)~;4x)BB z7Gr5m0U+~ILGul1}}Ikp?|C5LbICSFnGT^ln-co1DsH}}R?)ZdQGhMXXqpC5EL z2dtaz_&6CRLcNWd1y`A!O21C?T5+PxD^#jg=faPnm?gPn$uCm8G^pz`>KS@#tQc@i z<=a5Cue#29WPf9Y*@U9(rBSojN2e=o`~^6f zR?rSte5Q+;j$S%vh=s=uVsW(X7+>HrJLT||^ZN`Is(P@_?Ar7O6AGX1i^z=u;lzQr z1ZArk67-Z}5Ib%7{>(h#5VMS6j89EJSz_xnNG&knGtTicJtjNd1GoJn;DiYz{4vO9 zYD+5`8tm6*qaz&RFrtaMCSk?L?*t@d-r3*Y2xwom;*-~@Vpt8mT;Cm^)h76%*nRTT znHmT}D>fI(H8EnkP+`waf7FH@ybMyX1xu{UVFLtejcj{UXg|t} zinj*tymO??ZcaD(pM5^>QOn~>GWJ&5X>Kj+p_6NLJPp#<;w#ycdZw%TI5#>MY5@sv zJi1zJPJPv*u#!J}j2);avp3IG?NS>str+)BJk zTuc0cihbdmUhrd#f*iB?vzX%KUO(*ZsW5tUAhs&q`eYWl%lie0W^(T3b{|g`KF#`$ z>5qKvV)FqH7Hzkxbo1q)lP&+f1a8aq`8qoMJ)Y>8qrPyCQp2DtX#f26q(6LPuhgOr z(sCiBsVL*?hKaltWrFXxytOuAsk9c`3(@AF-k*Jks-<2#(d?mdWI0>!q+`@xilaKm zdpsEf5Ah9w6=K&Dvw@E$L-X@M1>B$>U8+--wlr(S9#Cz`b@%Yr&)l2uRK`=;{^k51 z7AlENl3i+cvFcG10>o}E3i2jJxdO|LU#rbmcqhn_b2sD(45VrRSSwPob4Pkr_cPdY zQ>D3ax=6b8J%tq0^tzVIsPI(Uv$>U+vDN%J7InjR&cr43NLpr$oaWrmVZ#$%*xu2^ zU~GWAvqDJy!P(|BAq8#>LVW)$fH;1$6rerB!A04B*)ve{tC@AyV%c}mJUGlQ!_T-Q zl=5E@)K_8cs9&Dj|3qj}>s(b#3Gg@`7FMi7mBwYE4Q>%n-?89Tseu(d1!f{GkbWdX zxtop_uhC0vb4$qY8o!&&qKkz-8c@0|n>q@I_F+)*NO0t$CogVFV6|;3H#}r<4i%lliNK6VRNl+7274{-&1!ayn*%xV% zrG09XXK_ukSXmR9k6@Wqnd!Zvr3YpvoN;}#J$Z>TF?N{b!FZNV+ZD(Q>y%(zj z8L6x*(7{t+sW>eB9`l}z6`ShPUX?}GgGk37X9ETrD6u?EVLGD(jb^*~GS?eUWa4(l zExn4@873PF>UPRKDyNJcv}xZ(4O40n=?GMM`~(> zM8(9)=Dt)Ru|Rmg#8+|Zf;H^91xAr%aau%(1N5BR)cOITOyNtTgR=oL_4qko&}WMh)nV?!0UU4O^hLA3M0rv!Z@Cw_YL44!w$F;E}c9&|z?yky{dfhV`2|E*(Od5&3yu6t_eq6>|pISct8O~Ws_=*#~ z8%0ZLcDHFAUrx81;pM(Gd>dP%8MnQHTK($(}g68?La72|5;xqC2xsj%`bNMfu z!UI)Xz7DT`vi*p&4-d!P(rNZde91|N$aj?y!g}JP$6sRul5eosH3su;`JBOOCL0s8FT`YSC;#G-&Fx}!NalMr zIo!wGW{WlVnSPFps0&ilZG&BKBa72h#vVqh(9q>4!!pn>W)ReG`615}4fV5Zo)&%R z@xq_!ZkPpy<8)q!bNR_ekKCY>vI`YI4v7pvvlKPaTAwM8A+Nh^JrHIgH_O<3`lii-JFmJ31hjtPlt_DoKLpx_}Jgyb^j%xLN*M$y~v$3Mebzqg0 zBZOv;Czchy5w%9@1Xb&E`Dh@cG>NKNXw;-A&KrBFK}657J(f#9vf`c0>?^VlL0837 zo$PR84Y(~5Tghe%yCaW=u_uux%SR?Z0#8c!an8LsmGWfgYY(jGBWj5QP1Ecqk2E4G zo-8zAq;4>!cKM%Xi?VY+x|zi4z!KG+c=g{@36m{DiUc|c-J z0-hiUkf}LVXsjXpew{LDQ#?gtYz9gP#%37{jns90;*aczWX#k+Qs8+Xtg>8;_8RD6 zJSalLuht2ab^y7;SJwGz7wz3l_0%U_-PLLbZG zxHZYZQ6?_$X9a10o+$<4Sp}sEw$|->Kib1LHkUQm z2}&a(;anpPcv@FhSW`8)lB_LGw^muyS73jgJDdgDmDkl7UB9yU25%NZ_*5t)=Yl~n z0AB*G@OXD+i=_GOm&xOr{G{M##aWRtg3Zl@?~@0-De=C?EOd9tn(*nr@8VVqnVc-| zC%=8~Ro#WwlpW8oqP%QI0DI|ENCG2E$?A{btOC?_aG=6MB_buud3~@BHnH=fy9&@PijVqcNFQa1MnfTl+!${Lk2w zL|5vs<|bWM)y}0NmqpHckQXK6;~x*es0Blwt9&v@2_kPH-q&-$Ji)MI34QMSWEZ`T zq<4L5qp z23T8WbN76ef|fzZ@_5-J^t^F~j`Z7&2qmLfR zOo1=Ya7H~#b;&aD6GLErDWC07b+(ye8wn<5vPTK_Xvv9J02i?um|-i@4hc}_Auo+i z@pxKty1auipLJFk5$TV)xDW;S-h|Jr!h~pfa-v3q&+7=QL0MHAM|nZxJ^(4c9MTu)|6VGNq8_*g?}PhNmR5KVFEUjsRU{BR zJI~eyFRIdfdvpCYBnK(mjMp=y_2nfQdFy4z(A!sg?^9u5Wy!1*t=BRlcL^0S)$W-# zO6re?f65qR1geI?Ifu#!(-`B#!87%`H)ycKYkR+aR9sYwUeNK#@o`QZs+=EJoY3 zu5r^V{9usF>1Ev}%ewb1$`TTB1Gxfb#eRd+50(j zN|BdHrKL|A2)wWoP|BZhk1`z}vFJQH^I)hWrjWulfe`SYi@AfWUkHsW=mgqRQRm2q zc!q5Xc0SgaKgZ}NZcw46S`JTbi6uU@N_qSV$+o@~>u_b`uBavST`c&E)~K<#y3oLS zMOelB-WQold5%r|J(FcpB*$8o2*($)v#J{v(ubyDb#@djbgCo01Xs>6v5ZP&XeAZ4j_%NRmk9@{zMclLW@-;8WG%)ZwdE{kaGQ!xXX8U2C9I ztG--)=eBhyKg$~x8Q6^lM2Ri@5-)I+;n)H);gVdK_p@!ZJp65lX6#$=M8aVq#6$9L*x49GS&9)w|MEI9C;ra!SIwpOh%j68Gxg z)I2I}NXXYr=*~wAEzi+^>Rd*flt11{MWkM?sILmu&k$G5g|*Wj?)wk*wyx;68<*5+ z=|oHEJ3f8O8fp_I+Z63*QDO#VDwU>^iou0QX?6`0ee7BUmZrxtD{QpuDn5zmu94uA z#bZcUrox1#>n~tsicq2Xgd%Y$=7%9=hFx)mAre(v^2HT}IdOoCqI$3UC3eRKg1V+bV}J_x`-Htu@Sg!z0w|^+PvdqW8>Pw*C3-J zUoXgLd6Snh6=Iz+y9DGgCAc8MxZreeGBbb3Ct~q;g%Mo|Vm2OrT6A418zcaJI@uB= z(YS4pJ{A@yy(HznuQBzO>@z%m>8eTbU4svP3~m^P7Y;)y!cC|c;@Z}Nzzq{^Xn7yg+S0JO%r?h;r6~PJy69KhHmrcM zbk@0Lt%BRYfmum?tfG8u6tdwr@8K`apSGdusRsELyRTDxl|&=@_Nw*ktb%MWu%pf| zcF}81-}}QeSz2Zsy`M5(9(%C_UvxnqfgYj^kmgd9VPuQu1D0~`v%3lM%SM?dw+nc- zQ*y$%p3aA&?7vII4;9P5O2k}XF5sW4Ezf^zo1lks?Ef2#?yX{vY%36}?uyCT3trNM zj#FHL1|f#b!M6p8`ZQDItL$>7&nev&Zdm_JdL>l&9b3EmBxwR zpxUO73?p^B@P=+~e$1=2o@SIhm`k>!C;YxYVKi3#B6FMr={tgS;7+CvMy_dxCr@^v zAEK?!G+bp1vN4sCaW6me=uy}F(QirXB)`s3uTHl zme%zKC`auPTeY_m{WsXZLiks-Aw1we!3I54-~Uvyf7j0+3j87lwt6;DE10FtLpl6c z&0kj6{JwxMDx@MLs>mc_V4|yPr(|FQHT+vZOxGN0<_Mtw-3R#9$?&_^OxNhXh7SH) zROok+pM{H)2fzYh=LCR3Kn?&11myXv*e?OoHG}F2S{Rub0D!-S3EJvCfCYHK+`q_& z2|my)UDJAS{;?mg@mh(Y-tI`H7CUtA$wTZ3Pai2ti=pfHe%tbI zhu_ct@69G^aSv04S{MP?)SwoE7Pinoj9;qNp!zUl+rPjP4?7eH;NkdvhwuOa4<``7 z4S6u}p+O+`aEkvqANYTGzn^}~{`<-e1Od3f58s7{2H|`q`0&iZ^B}>&c`wWUKs{Ld&>$e5`}5D!LoYjk6A1n#1q85j zaRWF&5CHrAR33UbIN5(Mm3w@LrHTdg0nQ6}nBhNz`QNqufIqSQeLKB-2r<2$F4V>n0AdAkvH}70#xR(bEicFap!?8p@MtjnQ{RI-|GoT#b?>)= zrO~fB|C;?@c$xnnW#eFDV2FAj40crD-v@w`g9E|=Fa-SOfk5DgpgkM_i@$h4FgxeH zy8q!pIPT#!|K8n}mhoeOP}j aiQn)XT^rah?}9)a;Cqlcji{U$>i-Ag>Xu;u literal 0 HcmV?d00001 diff --git a/tests/data/DropX/figs/dx4.dot b/tests/data/DropX/figs/dx4.dot new file mode 100644 index 0000000..6149615 --- /dev/null +++ b/tests/data/DropX/figs/dx4.dot @@ -0,0 +1,26 @@ +strict digraph "" { + in1 -> "in1_METER_(%)_77"; + pico_in1 -> "pico_in1_MIX_(+)_pico_in2_73"; + pico_in2 -> "pico_in1_MIX_(+)_pico_in2_73"; + pico_in3 -> "pico_in1_MIX_(+)_pico_in2_73_MIX_(+)_pico_in3_74"; + pico_in3 -> "pico_in1_MIX_(+)_pico_in2_73_MIX_(+)_pico_in3_74_MIX_(+)_pico_in3_75"; + pico_in4 -> "pico_in1_MIX_(+)_pico_in2_73_MIX_(+)_pico_in3_74_MIX_(+)_pico_in3_75_MIX_(+)_pico_in4_76"; + pico_mix -> "droplets_MIX_(+)_pico_mix_78"; + "pico_in1_MIX_(+)_pico_in2_73" -> "pico_in1_MIX_(+)_pico_in2_73_MIX_(+)_pico_in3_74"; + "pico_in1_MIX_(+)_pico_in2_73_MIX_(+)_pico_in3_74" -> "pico_in1_MIX_(+)_pico_in2_73_MIX_(+)_pico_in3_74_MIX_(+)_pico_in3_75"; + "pico_in1_MIX_(+)_pico_in2_73_MIX_(+)_pico_in3_74_MIX_(+)_pico_in3_75" -> "pico_in1_MIX_(+)_pico_in2_73_MIX_(+)_pico_in3_74_MIX_(+)_pico_in3_75_MIX_(+)_pico_in4_76"; + "pico_in1_MIX_(+)_pico_in2_73_MIX_(+)_pico_in3_74_MIX_(+)_pico_in3_75_MIX_(+)_pico_in4_76" -> pico_mix; + droplets -> "droplets_MIX_(+)_pico_mix_78"; + "in1_METER_(%)_77" -> droplets; + val_1 -> "in1_METER_(%)_77"; + injected_droplets -> "injected_droplets_SIEVE_(-)_sort_waste_temp_79"; + "droplets_MIX_(+)_pico_mix_78" -> injected_droplets; + sort_keep_temp -> "sort_keep_temp_SIEVE_(-)_sort_waste1_80"; + sort_waste_temp -> "sort_waste2_SIEVE_(-)_sort_waste_temp_81"; + "injected_droplets_SIEVE_(-)_sort_waste_temp_79" -> sort_keep_temp; + "injected_droplets_SIEVE_(-)_sort_waste_temp_79" -> sort_waste_temp; + "sort_keep_temp_SIEVE_(-)_sort_waste1_80" -> sort_keep1; + "sort_keep_temp_SIEVE_(-)_sort_waste1_80" -> sort_waste1; + "sort_waste2_SIEVE_(-)_sort_waste_temp_81" -> sort_keep2; + "sort_waste2_SIEVE_(-)_sort_waste_temp_81" -> sort_waste2; +} diff --git a/tests/data/DropX/figs/dx4.dot.pdf b/tests/data/DropX/figs/dx4.dot.pdf new file mode 100644 index 0000000000000000000000000000000000000000..2d8c7c23793b65c5375f47eff0e29be21cb3e8c3 GIT binary patch literal 15500 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 F{|B;`j>G@} literal 0 HcmV?d00001 diff --git a/tests/data/DropX/figs/dx5.dot b/tests/data/DropX/figs/dx5.dot new file mode 100644 index 0000000..3740e0b --- /dev/null +++ b/tests/data/DropX/figs/dx5.dot @@ -0,0 +1,19 @@ +strict digraph "" { + in1 -> "in1_METER_(%)_82"; + in2 -> "in1_METER_(%)_82_MIX_(+)_in2_83"; + injected_droplets -> "injected_droplets_DIVIDE_(/)_84"; + "in1_METER_(%)_82" -> "in1_METER_(%)_82_MIX_(+)_in2_83"; + val_1 -> "in1_METER_(%)_82"; + "in1_METER_(%)_82_MIX_(+)_in2_83" -> injected_droplets; + split1 -> "split1_PROCESS_(~)_85"; + split2 -> "split2_PROCESS_(~)_87"; + "injected_droplets_DIVIDE_(/)_84" -> split1; + "injected_droplets_DIVIDE_(/)_84" -> split2; + val_2 -> "injected_droplets_DIVIDE_(/)_84"; + "split1_PROCESS_(~)_85" -> "sort_waste1_SIEVE_(-)_split1_PROCESS_(~)_85_86"; + "sort_waste1_SIEVE_(-)_split1_PROCESS_(~)_85_86" -> sort_keep1; + "sort_waste1_SIEVE_(-)_split1_PROCESS_(~)_85_86" -> sort_waste1; + "split2_PROCESS_(~)_87" -> "sort_waste2_SIEVE_(-)_split2_PROCESS_(~)_87_88"; + "sort_waste2_SIEVE_(-)_split2_PROCESS_(~)_87_88" -> sort_keep2; + "sort_waste2_SIEVE_(-)_split2_PROCESS_(~)_87_88" -> sort_waste2; +} diff --git a/tests/data/DropX/figs/dx5.dot.pdf b/tests/data/DropX/figs/dx5.dot.pdf new file mode 100644 index 0000000000000000000000000000000000000000..dea3cad7bf0a79f94079828f8111bdecaf49d170 GIT binary patch literal 14301 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 literal 0 HcmV?d00001 diff --git a/tests/data/DropX/figs/dx6.dot b/tests/data/DropX/figs/dx6.dot new file mode 100644 index 0000000..02407aa --- /dev/null +++ b/tests/data/DropX/figs/dx6.dot @@ -0,0 +1,14 @@ +strict digraph "" { + in1 -> "in1_MIX_(+)_in2_89"; + in2 -> "in1_MIX_(+)_in2_89"; + mix -> "mix_METER_(%)_90"; + "in1_MIX_(+)_in2_89" -> mix; + droplets -> "droplets_DIVIDE_(/)_91"; + "mix_METER_(%)_90" -> droplets; + val_1 -> "mix_METER_(%)_90"; + split1 -> out1; + split2 -> out2; + "droplets_DIVIDE_(/)_91" -> split1; + "droplets_DIVIDE_(/)_91" -> split2; + val_2 -> "droplets_DIVIDE_(/)_91"; +} diff --git a/tests/data/DropX/figs/dx6.dot.pdf b/tests/data/DropX/figs/dx6.dot.pdf new file mode 100644 index 0000000000000000000000000000000000000000..c5d5c7db6e07cb797b30d032404bbdabf5d63336 GIT binary patch literal 11512 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 literal 0 HcmV?d00001 diff --git a/tests/data/DropX/figs/dx7.dot b/tests/data/DropX/figs/dx7.dot new file mode 100644 index 0000000..6f4e9d1 --- /dev/null +++ b/tests/data/DropX/figs/dx7.dot @@ -0,0 +1,18 @@ +strict digraph "" { + in1 -> "in1_METER_(%)_92"; + in2 -> "in2_METER_(%)_93"; + pico_in1 -> "pico_in1_MIX_(+)_pico_in2_95"; + pico_in2 -> "pico_in1_MIX_(+)_pico_in2_95"; + merged_droplets -> "merged_droplets_MIX_(+)_pico_in1_MIX_(+)_pico_in2_95_96"; + injected_droplets -> "injected_droplets_DIVIDE_(/)_97"; + "in1_METER_(%)_92" -> "in1_METER_(%)_92_MIX_(+)_in2_METER_(%)_93_94"; + val_1 -> "in1_METER_(%)_92"; + "in2_METER_(%)_93" -> "in1_METER_(%)_92_MIX_(+)_in2_METER_(%)_93_94"; + val_2 -> "in2_METER_(%)_93"; + "in1_METER_(%)_92_MIX_(+)_in2_METER_(%)_93_94" -> merged_droplets; + "pico_in1_MIX_(+)_pico_in2_95" -> "merged_droplets_MIX_(+)_pico_in1_MIX_(+)_pico_in2_95_96"; + "merged_droplets_MIX_(+)_pico_in1_MIX_(+)_pico_in2_95_96" -> injected_droplets; + "injected_droplets_DIVIDE_(/)_97" -> out1; + "injected_droplets_DIVIDE_(/)_97" -> out2; + val_3 -> "injected_droplets_DIVIDE_(/)_97"; +} diff --git a/tests/data/DropX/figs/dx7.dot.pdf b/tests/data/DropX/figs/dx7.dot.pdf new file mode 100644 index 0000000000000000000000000000000000000000..377e92610e17e8110ba65f8f62f354a0b721b6e9 GIT binary patch literal 12901 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 "in1_MIX_(+)_in2_98"; + in2 -> "in1_MIX_(+)_in2_98"; + in3 -> "in1_MIX_(+)_in2_98_MIX_(+)_in3_99"; + in4 -> "droplets_MIX_(+)_in4_101"; + in5 -> "droplets_MIX_(+)_in4_101_MIX_(+)_in5_102"; + in6 -> "droplets_MIX_(+)_in4_101_MIX_(+)_in5_102_MIX_(+)_in6_103"; + in_mix -> "in_mix_METER_(%)_100"; + "in1_MIX_(+)_in2_98" -> "in1_MIX_(+)_in2_98_MIX_(+)_in3_99"; + "in1_MIX_(+)_in2_98_MIX_(+)_in3_99" -> in_mix; + droplets -> "droplets_MIX_(+)_in4_101"; + "in_mix_METER_(%)_100" -> droplets; + val_1 -> "in_mix_METER_(%)_100"; + injected_droplets -> "injected_droplets_PROCESS_(~)_104"; + "droplets_MIX_(+)_in4_101" -> "droplets_MIX_(+)_in4_101_MIX_(+)_in5_102"; + "droplets_MIX_(+)_in4_101_MIX_(+)_in5_102" -> "droplets_MIX_(+)_in4_101_MIX_(+)_in5_102_MIX_(+)_in6_103"; + "droplets_MIX_(+)_in4_101_MIX_(+)_in5_102_MIX_(+)_in6_103" -> injected_droplets; + mixed_droplets -> "mixed_droplets_SIEVE_(-)_out2_105"; + "injected_droplets_PROCESS_(~)_104" -> mixed_droplets; + temp -> "out1_SIEVE_(-)_temp_106"; + "mixed_droplets_SIEVE_(-)_out2_105" -> out2; + "mixed_droplets_SIEVE_(-)_out2_105" -> temp; + "out1_SIEVE_(-)_temp_106" -> out1; + "out1_SIEVE_(-)_temp_106" -> out3; +} diff --git a/tests/data/DropX/figs/dx8.dot.pdf b/tests/data/DropX/figs/dx8.dot.pdf new file mode 100644 index 0000000000000000000000000000000000000000..7b55f7f81a0de8f7d0be4400c142224d877ed39a GIT binary patch literal 14916 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 VWB13pbPS9PtWZQmg3>}z{|A)pVIu$l literal 0 HcmV?d00001 diff --git a/tests/data/DropX/figs/dx9.dot b/tests/data/DropX/figs/dx9.dot new file mode 100644 index 0000000..53e8516 --- /dev/null +++ b/tests/data/DropX/figs/dx9.dot @@ -0,0 +1,15 @@ +strict digraph "" { + in1 -> "in1_METER_(%)_107"; + in2 -> "in2_METER_(%)_108"; + in3 -> "in3_MIX_(+)_merged_droplets_110"; + merged_droplets -> "in3_MIX_(+)_merged_droplets_110"; + "in1_METER_(%)_107" -> "in1_METER_(%)_107_MIX_(+)_in2_METER_(%)_108_109"; + val_1 -> "in1_METER_(%)_107"; + "in2_METER_(%)_108" -> "in1_METER_(%)_107_MIX_(+)_in2_METER_(%)_108_109"; + val_2 -> "in2_METER_(%)_108"; + "in1_METER_(%)_107_MIX_(+)_in2_METER_(%)_108_109" -> merged_droplets; + injected_droplets -> "injected_droplets_SIEVE_(-)_out1_111"; + "in3_MIX_(+)_merged_droplets_110" -> injected_droplets; + "injected_droplets_SIEVE_(-)_out1_111" -> out1; + "injected_droplets_SIEVE_(-)_out1_111" -> out2; +} diff --git a/tests/data/DropX/figs/dx9.dot.pdf b/tests/data/DropX/figs/dx9.dot.pdf new file mode 100644 index 0000000000000000000000000000000000000000..f3ab7dbeaa5be1c029d0983dde5ec0ffe682da67 GIT binary patch literal 12325 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 EKX(Z=XaE2J literal 0 HcmV?d00001 diff --git a/tests/data/DropX/netlists/Standard Sizes.md b/tests/data/DropX/netlists/Standard Sizes.md new file mode 100644 index 0000000..4deff71 --- /dev/null +++ b/tests/data/DropX/netlists/Standard Sizes.md @@ -0,0 +1,101 @@ +# NOZZLE DROPLET GENERATOR + +``` +orificeSize=150 +orificeLength=375 +oilInputWidth=600 +waterInputWidth=375 +outputWidth=300 +outputLength=5000 +height=300 +``` + +# MIXER + +``` +bendSpacing=600 +numberOfBends=5 +channelWidth=300 +bendLength=2000 +height=300 +``` + +# DROPLET SORTER + +``` +height=300 +inletWidth=300 +inletLength=4000 +inletLength=4000 +electrodeDistance=1000 +electrodeWidth=700 +electrodeLength=5000 +outletWidth=300 +angle=45 +wasteWidth=600 +outputLength=4000 +keepWidth=600 +pressureWidth=1000 +numberofDistributors=5 +channelDepth=300 +electrodeDepth=300 +pressureDepth=200 +``` + +# DROPLET CAPACITANCE SENSOR + +``` +height=300 +inletWidth=300 +inletLength=10000 +electrodeWidth=1500 +electrodeLength=4000 +electrodeDistance=500 +sensorWidth=1000 +sensorLength=3000 +channelDepth=300 +electrodeDepth=300 +``` + +# PICOINJECTOR + +``` +height=300 +injectorWidth=1000 +width=10000 +injectorWidth=1000 +injectorLength=5000 +dropletWidth=100 +nozzleWidth=50 +nozzleLength=500 +electrodeDistance=500 +electrodeWidth=800 +electrodeLength=3000 +``` + +# DROPLET SPLITTER +``` +height=30 +inletWidth=300 +inletLength=2000 +outletWidth1=300 +outletLength1=2000 +outletWidth2=300 +outletLength2=2000 +``` + +# DROPLET MERGER +``` +height=200 +inletWidth=300 +inletLength=4000 +electrodeWidth=1000 +electrodeLength=5000 +electrodeDistance=800 +outletWidth=300 +outletLength=4000 +chamberHeight=800 +chamberLength=800 +channelDepth=300 +electrodeDepth=200 +``` \ No newline at end of file diff --git a/tests/data/DropX/netlists/dx10_ref.mint b/tests/data/DropX/netlists/dx10_ref.mint new file mode 100644 index 0000000..fbcd0ae --- /dev/null +++ b/tests/data/DropX/netlists/dx10_ref.mint @@ -0,0 +1,68 @@ +DEVICE dx10_ref + +LAYER FLOW + +PORT port_oil1 portRadius=2000; +PORT port_water1 portRadius=2000; +PORT port_oil2 portRadius=2000; + +NOZZLE DROPLET GENERATOR nozzle_droplet_generator_1 + orificeSize=150 + orificeLength=375 + oilInputWidth=600 + waterInputWidth=375 + outputWidth=300 + outputLength=5000 + height=300; + +CHANNEL connection_1 from port_oil1 to nozzle_droplet_generator_1 1 channelWidth=300; +CHANNEL connection_2 from port_water1 to nozzle_droplet_generator_1 4 channelWidth=300; +CHANNEL connection_3 from port_oil2 to nozzle_droplet_generator_1 3 channelWidth=300; + +PORT port_injector1 portRadius=2000; + +PICOINJECTOR pico_injector_1 + height=300 + injectorWidth=1000 + width=10000 + injectorWidth=1000 + injectorLength=5000 + dropletWidth=100 + nozzleWidth=50 + nozzleLength=500 + electrodeDistance=500 + electrodeWidth=800 + electrodeLength=3000; + +CHANNEL connection_4 from nozzle_droplet_generator_1 2 to pico_injector_1 1 channelWidth=300; +CHANNEL connection_5 from port_injector1 to pico_injector_1 3 channelWidth=300; + +DROPLET SORTER droplet_sorter_1 + height=300 + inletWidth=300 + inletLength=4000 + inletLength=4000 + electrodeDistance=1000 + electrodeWidth=700 + electrodeLength=5000 + outletWidth=300 + angle=45 + wasteWidth=600 + outputLength=4000 + keepWidth=600 + pressureWidth=1000 + numberofDistributors=5 + channelDepth=300 + electrodeDepth=300 + pressureDepth=200; + +CHANNEL connection_6 from pico_injector_1 2 to droplet_sorter_1 1 channelWidth=300; + +PORT port_out_1 portRadius=2000; +PORT port_out_2 portRadius=2000; + +CHANNEL connection_7 from droplet_sorter_1 2 to port_out_1 channelWidth=300; +CHANNEL connection_8 from droplet_sorter_1 3 to port_out_2 channelWidth=300; + +END LAYER + diff --git a/tests/data/DropX/netlists/dx11_ref.mint b/tests/data/DropX/netlists/dx11_ref.mint new file mode 100644 index 0000000..4fac312 --- /dev/null +++ b/tests/data/DropX/netlists/dx11_ref.mint @@ -0,0 +1,135 @@ +DEVICE dx11_ref + +LAYER FLOW + +PORT port_oil1 portRadius=2000; +PORT port_water1 portRadius=2000; +PORT port_oil2 portRadius=2000; + +NOZZLE DROPLET GENERATOR nozzle_droplet_generator_1 + orificeSize=150 + orificeLength=375 + oilInputWidth=600 + waterInputWidth=375 + outputWidth=300 + outputLength=5000 + height=300; + +CHANNEL connection_1 from port_oil1 to nozzle_droplet_generator_1 1 channelWidth=300; +CHANNEL connection_2 from port_water1 to nozzle_droplet_generator_1 4 channelWidth=300; +CHANNEL connection_3 from port_oil2 to nozzle_droplet_generator_1 3 channelWidth=300; + + +PORT port_injector1 portRadius=2000; +PICOINJECTOR pico_injector_1 + height=300 + injectorWidth=1000 + width=10000 + injectorWidth=1000 + injectorLength=5000 + dropletWidth=100 + nozzleWidth=50 + nozzleLength=500 + electrodeDistance=500 + electrodeWidth=800 + electrodeLength=3000; + +CHANNEL connection_4 from port_injector1 to pico_injector_1 3 channelWidth=300; + +MIXER mix_1 + bendSpacing=600 + numberOfBends=5 + channelWidth=300 + bendLength=2000 + height=300; + +CHANNEL connection_5 from pico_injector_1 2 to mix_1 1 channelWidth=300; +CHANNEL connection_6 from nozzle_droplet_generator_1 2 to pico_injector_1 1 channelWidth=300; + +DROPLET SORTER droplet_sorter_1 + inletWidth=300 + inletLength=4000 + inletLength=4000 + electrodeDistance=1000 + electrodeWidth=700 + electrodeLength=5000 + outletWidth=300 + angle=45 + wasteWidth=600 + outputLength=4000 + keepWidth=600 + pressureWidth=1000 + numberofDistributors=5 + channelDepth=300 + electrodeDepth=300 + pressureDepth=200; + +CHANNEL connection_7 from mix_1 2 to droplet_sorter_1 1 channelWidth=300; + +MIXER mix_2 + bendSpacing=600 + numberOfBends=5 + channelWidth=300 + bendLength=2000 + height=300; + +CHANNEL connection_8 from droplet_sorter_1 2 to mix_2 1 channelWidth=300; + +PORT port_out_waste1 portRadius=2000; +CHANNEL connection_15 from droplet_sorter_1 2 to port_out_waste1 channelWidth=300; + +PORT port_injector2 portRadius=2000; +PICOINJECTOR pico_injector_2 + height=300 + injectorWidth=1000 + width=10000 + injectorWidth=1000 + injectorLength=5000 + dropletWidth=100 + nozzleWidth=50 + nozzleLength=500 + electrodeDistance=500 + electrodeWidth=800 + electrodeLength=3000; + +CHANNEL connection_9 from mix_2 2 to pico_injector_2 1 channelWidth=300; +CHANNEL connection_10 from port_injector2 to pico_injector_2 3 channelWidth=300; + +MIXER mix_3 + bendSpacing=600 + numberOfBends=5 + channelWidth=300 + bendLength=2000 + height=300; + +CHANNEL connection_11 from pico_injector_2 2 to mix_3 1 channelWidth=300; + +DROPLET SORTER droplet_sorter_2 + height=300 + inletWidth=300 + inletLength=4000 + inletLength=4000 + electrodeDistance=1000 + electrodeWidth=700 + electrodeLength=5000 + outletWidth=300 + angle=45 + wasteWidth=600 + outputLength=4000 + keepWidth=600 + pressureWidth=1000 + numberofDistributors=5 + channelDepth=300 + electrodeDepth=300 + pressureDepth=200; + +CHANNEL connection_12 from mix_3 2 to droplet_sorter_2 1 channelWidth=300; + +PORT port_out_waste2 portRadius=2000; +PORT port_out_keep portRadius=2000; + +CHANNEL connection_13 from droplet_sorter_2 2 to port_out_waste2 channelWidth=300; +CHANNEL connection_14 from droplet_sorter_2 3 to port_out_keep channelWidth=300; + +END LAYER + diff --git a/tests/data/DropX/netlists/dx12_ref.mint b/tests/data/DropX/netlists/dx12_ref.mint new file mode 100644 index 0000000..957f59d --- /dev/null +++ b/tests/data/DropX/netlists/dx12_ref.mint @@ -0,0 +1,73 @@ +DEVICE dx12_ref + +LAYER FLOW + +PORT port_oil1 portRadius=2000; +PORT port_water1 portRadius=2000; +PORT port_oil2 portRadius=2000; +NOZZLE DROPLET GENERATOR nozzle_droplet_generator_1 + orificeSize=150 + orificeLength=375 + oilInputWidth=600 + waterInputWidth=375 + outputWidth=300 + outputLength=5000 + height=300; + +CHANNEL connection_1 from port_oil1 to nozzle_droplet_generator_1 1 channelWidth=300 channelWidth=300; +CHANNEL connection_2 from port_water1 to nozzle_droplet_generator_1 4 channelWidth=300 channelWidth=300; +CHANNEL connection_3 from port_oil2 to nozzle_droplet_generator_1 3 channelWidth=300 channelWidth=300; + +PORT port_injector portRadius=2000; +PICOINJECTOR pico_injector_1 + height=300 + injectorWidth=1000 + width=10000 + injectorWidth=1000 + injectorLength=5000 + dropletWidth=100 + nozzleWidth=50 + nozzleLength=500 + electrodeDistance=500 + electrodeWidth=800 + electrodeLength=3000; + +CHANNEL connection_4 from port_injector to pico_injector_1 3 channelWidth=300; +CHANNEL connection_5 from nozzle_droplet_generator_1 2 to pico_injector_1 1 channelWidth=300; + +MIXER mixer_1 + bendSpacing=600 + numberOfBends=5 + channelWidth=300 + bendLength=2000 + height=300; + +CHANNEL connection_6 from pico_injector_1 2 to mixer_1 1 channelWidth=300; + +PORT port_out_waste portRadius=2000; +PORT port_out_keep portRadius=2000; +DROPLET SORTER droplet_sorter_1 + height=300 + inletWidth=300 + inletLength=4000 + inletLength=4000 + electrodeDistance=1000 + electrodeWidth=700 + electrodeLength=5000 + outletWidth=300 + angle=45 + wasteWidth=600 + outputLength=4000 + keepWidth=600 + pressureWidth=1000 + numberofDistributors=5 + channelDepth=300 + electrodeDepth=300 + pressureDepth=200; + +CHANNEL connection_7 from mixer_1 2 to droplet_sorter_1 1 channelWidth=300; +CHANNEL connection_8 from droplet_sorter_1 2 to port_out_waste channelWidth=300; +CHANNEL connection_9 from droplet_sorter_1 3 to port_out_keep channelWidth=300; + +END LAYER + diff --git a/tests/data/DropX/netlists/dx13_ref.mint b/tests/data/DropX/netlists/dx13_ref.mint new file mode 100644 index 0000000..ffa05bb --- /dev/null +++ b/tests/data/DropX/netlists/dx13_ref.mint @@ -0,0 +1,138 @@ +DEVICE dx13_ref + +LAYER FLOW + +PORT port_oil1, port_oil2, port_water1 portRadius=2000; +NOZZLE DROPLET GENERATOR nozzle_droplet_generator_1 orificeSize=150 +orificeLength=375 +oilInputWidth=600 +waterInputWidth=375 +outputWidth=300 +outputLength=5000 +height=300; + +CHANNEL c1 from port_oil1 to nozzle_droplet_generator_1 1 channelWidth=300; +CHANNEL c2 from port_oil2 to nozzle_droplet_generator_1 3 channelWidth=300; +CHANNEL c3 from port_water1 to nozzle_droplet_generator_1 4 channelWidth=300; + +MIXER mixer_1 bendSpacing=600 +numberOfBends=5 +channelWidth=300 +bendLength=2000 +height=300 +; + +CHANNEL c4 from nozzle_droplet_generator_1 to mixer_1 1 channelWidth=300; + +PORT port_injector1 portRadius=2000; +PICOINJECTOR pico_injector_1 height=300 +injectorWidth=1000 +width=10000 +injectorWidth=1000 +injectorLength=5000 +dropletWidth=100 +nozzleWidth=50 +nozzleLength=500 +electrodeDistance=500 +electrodeWidth=800 +electrodeLength=3000; + +CHANNEL c5 from port_injector1 to pico_injector_1 3 channelWidth=300; + +CHANNEL c6 from mixer_1 2 to pico_injector_1 1 channelWidth=300; + +MIXER mixer_2 bendSpacing=600 +numberOfBends=5 +channelWidth=300 +bendLength=2000 +height=300 +; + +CHANNEL c7 from pico_injector_1 2 to mixer_2 1 channelWidth=300 channelWidth=300; + +PORT port_waste1 portRadius=2000; +DROPLET SORTER droplet_sorter_1 height=300 +inletWidth=300 +inletLength=4000 +inletLength=4000 +electrodeDistance=1000 +electrodeWidth=700 +electrodeLength=5000 +outletWidth=300 +angle=45 +wasteWidth=600 +outputLength=4000 +keepWidth=600 +pressureWidth=1000 +numberofDistributors=5 +channelDepth=300 +electrodeDepth=300 +pressureDepth=200; + +CHANNEL c11 from droplet_sorter_1 2 to port_waste1 channelWidth=300; + +CHANNEL c8 from mixer_2 2 to droplet_sorter_1 1 channelWidth=300; + +PORT port_injector2 portRadius=2000; +PICOINJECTOR pico_injector_2 height=300 +injectorWidth=1000 +width=10000 +injectorWidth=1000 +injectorLength=5000 +dropletWidth=100 +nozzleWidth=50 +nozzleLength=500 +electrodeDistance=500 +electrodeWidth=800 +electrodeLength=3000; + +CHANNEL c9 from port_injector2 to pico_injector_2 3 channelWidth=300; +CHANNEL c10 from droplet_sorter_1 3 to pico_injector_2 1 channelWidth=300; + +MIXER mixer_3 bendSpacing=600 +numberOfBends=5 +channelWidth=300 +bendLength=2000 +height=300 +; + +CHANNEL c10 from pico_injector_2 2 to mixer_3 1 channelWidth=300; + +PORT port_waste2 portRadius=2000; +DROPLET SORTER droplet_sorter_2 height=300 +inletWidth=300 +inletLength=4000 +inletLength=4000 +electrodeDistance=1000 +electrodeWidth=700 +electrodeLength=5000 +outletWidth=300 +angle=45 +wasteWidth=600 +outputLength=4000 +keepWidth=600 +pressureWidth=1000 +numberofDistributors=5 +channelDepth=300 +electrodeDepth=300 +pressureDepth=200; + +CHANNEL c12 from mixer_3 2 to droplet_sorter_2 1 channelWidth=300; +CHANNEL c13 from droplet_sorter_2 2 to port_waste2 channelWidth=300; + +DROPLET SPLITTER droplet_splitter_1 height=30 +inletWidth=300 +inletLength=2000 +outletWidth1=300 +outletLength1=2000 +outletWidth2=300 +outletLength2=2000; +CHANNEL c14 from droplet_sorter_2 3 to droplet_splitter_1 1 channelWidth=300; + +PORT port_out1, port_out2 portRadius=2000; +CHANNEL c15 from droplet_splitter_1 2 to port_out1 channelWidth=300; +CHANNEL c16 from droplet_splitter_1 3 to port_out2 channelWidth=300 channelWidth=300; + + +END LAYER + diff --git a/tests/data/DropX/netlists/dx14_ref.mint b/tests/data/DropX/netlists/dx14_ref.mint new file mode 100644 index 0000000..666be27 --- /dev/null +++ b/tests/data/DropX/netlists/dx14_ref.mint @@ -0,0 +1,73 @@ +DEVICE dx14_ref + +LAYER FLOW + +PORT port_in1, port_in2, port_in3 portRadius=2000; +MIXER mixer_in bendSpacing=600 +numberOfBends=5 +channelWidth=300 +bendLength=2000 +height=300; + +CHANNEL channel_1 from port_in1 to mixer_in 1 channelWidth=300; +CHANNEL channel_2 from port_in2 to mixer_in 1 channelWidth=300; +CHANNEL channel_3 from port_in3 to mixer_in 1 channelWidth=300; + +NOZZLE DROPLET GENERATOR nozzle_droplet_generator1 orificeSize=150 +orificeLength=375 +oilInputWidth=600 +waterInputWidth=375 +outputWidth=300 +outputLength=5000 +height=300; +NOZZLE DROPLET GENERATOR nozzle_droplet_generator2 orificeSize=150 +orificeLength=375 +oilInputWidth=600 +waterInputWidth=375 +outputWidth=300 +outputLength=5000 +height=300; + +CHANNEL channel_mixed_1 from mixer_in 2 to nozzle_droplet_generator1 4 channelWidth=300; +CHANNEL channel_mixed_2 from mixer_in 2 to nozzle_droplet_generator2 4 channelWidth=300; + +PORT port_oil1, port_oil2, port_oil3, port_oil4 portRadius=2000; +CHANNEL channel_oil1 from port_oil1 to nozzle_droplet_generator1 1 channelWidth=300; +CHANNEL channel_oil2 from port_oil2 to nozzle_droplet_generator1 3 channelWidth=300; +CHANNEL channel_oil3 from port_oil3 to nozzle_droplet_generator2 1 channelWidth=300; +CHANNEL channel_oil4 from port_oil4 to nozzle_droplet_generator2 3 channelWidth=300; + +MIXER mixer_incubate1, mixer_incubate2 bendSpacing=600 +numberOfBends=5 +channelWidth=300 +bendLength=2000 +height=300; +CHANNEL channel_incubate1 from nozzle_droplet_generator1 2 to mixer_incubate1 1 channelWidth=300; +CHANNEL channel_incubate2 from nozzle_droplet_generator2 2 to mixer_incubate2 1 channelWidth=300; + +DROPLET SORTER droplet_sorter1 height=300 +inletWidth=300 +inletLength=4000 +inletLength=4000 +electrodeDistance=1000 +electrodeWidth=700 +electrodeLength=5000 +outletWidth=300 +angle=45 +wasteWidth=600 +outputLength=4000 +keepWidth=600 +pressureWidth=1000 +numberofDistributors=5 +channelDepth=300 +electrodeDepth=300 +pressureDepth=200 ; +CHANNEL channel_incubate3 from mixer_incubate1 1 to droplet_sorter1 1 channelWidth=300; +CHANNEL channel_incubate4 from mixer_incubate2 1 to droplet_sorter1 1 channelWidth=300; + +PORT port_out1, port_out2 portRadius=2000; +CHANNEL channel_out1 from droplet_sorter1 2 to port_out1 channelWidth=300; +CHANNEL channel_out2 from droplet_sorter1 3 to port_out2 channelWidth=300; + +END LAYER + diff --git a/tests/data/DropX/netlists/dx15_ref.mint b/tests/data/DropX/netlists/dx15_ref.mint new file mode 100644 index 0000000..113f091 --- /dev/null +++ b/tests/data/DropX/netlists/dx15_ref.mint @@ -0,0 +1,43 @@ +DEVICE dx15_ref + +LAYER FLOW + +PORT port_in1, port_in2, port_in3 portRadius=2000 ; + +MIXER mixer1 bendSpacing=600 +numberOfBends=5 +channelWidth=300 +bendLength=2000 +height=300 ; + +CHANNEL c1 from port_in1 to mixer1 1 channelWidth=300 ; +CHANNEL c2 from port_in2 to mixer1 1 channelWidth=300 ; +CHANNEL c3 from port_in3 to mixer1 1 channelWidth=300 ; + +DROPLET SORTER droplet_sorter1 height=300 +inletWidth=300 +inletLength=4000 +inletLength=4000 +electrodeDistance=1000 +electrodeWidth=700 +electrodeLength=5000 +outletWidth=300 +angle=45 +wasteWidth=600 +outputLength=4000 +keepWidth=600 +pressureWidth=1000 +numberofDistributors=5 +channelDepth=300 +electrodeDepth=300 +pressureDepth=200; + +CHANNEL c4 from mixer1 2 to droplet_sorter1 1 channelWidth=300; + +PORT port_out_keep, port_out_waste portRadius=2000; + +CHANNEL c5 from droplet_sorter1 3 to port_out_keep 1 channelWidth=300; +CHANNEL c6 from droplet_sorter1 2 to port_out_waste 1 channelWidth=300; + +END LAYER + diff --git a/tests/data/DropX/netlists/dx1_ref.mint b/tests/data/DropX/netlists/dx1_ref.mint new file mode 100644 index 0000000..1ee0034 --- /dev/null +++ b/tests/data/DropX/netlists/dx1_ref.mint @@ -0,0 +1,42 @@ +DEVICE dx1_ref + +LAYER FLOW + +PORT port_oil1 portRadius=2000 ; +PORT port_oil2 portRadius=2000 ; +PORT port_water portRadius=2000 ; +PORT port_injector portRadius=2000 ; +PORT port_out_waste portRadius=2000 ; +PORT port_out_keep portRadius=2000 ; + +NOZZLE DROPLET GENERATOR nozzle_droplet_generator_1 orificeSize=150 orificeLength=375 + oilInputWidth=600 waterInputWidth=375 outputWidth=300 outputLength=5000 height=300 ; + +PICOINJECTOR picoinjector_1 height=300 + injectorWidth=1000 + width=10000 + injectorWidth=1000 + injectorLength=5000 + dropletWidth=100 + nozzleWidth=50 + nozzleLength=500 + electrodeDistance=500 + electrodeWidth=800 + electrodeLength=3000; + +MIXER mixer_1 bendSpacing=600 numberOfBends=5 channelWidth=300 bendLength=2000 height=300; + +DROPLET SORTER droplet_sorter_1 ; + +CHANNEL connection_1 from port_oil1 to nozzle_droplet_generator_1 1 channelWidth=300 ; +CHANNEL connection_2 from port_oil2 to nozzle_droplet_generator_1 3 channelWidth=300 ; +CHANNEL connection_3 from port_water to nozzle_droplet_generator_1 4 channelWidth=300 ; +CHANNEL connection_4 from port_injector to picoinjector_1 3 channelWidth=300 ; +CHANNEL connection_5 from nozzle_droplet_generator_1 2 to picoinjector_1 1 channelWidth=300 ; +CHANNEL connection_6 from picoinjector_1 2 to mixer_1 1 channelWidth=300 ; +CHANNEL connection_7 from mixer_1 2 to droplet_sorter_1 1 channelWidth=300 ; +CHANNEL connection_8 from droplet_sorter_1 2 to port_out_waste channelWidth=300 ; +CHANNEL connection_9 from droplet_sorter_1 3 to port_out_keep channelWidth=300 ; + +END LAYER + diff --git a/tests/data/DropX/netlists/dx2_ref.mint b/tests/data/DropX/netlists/dx2_ref.mint new file mode 100644 index 0000000..22a530f --- /dev/null +++ b/tests/data/DropX/netlists/dx2_ref.mint @@ -0,0 +1,59 @@ +DEVICE dx2_ref + +LAYER FLOW + +PORT port_oil1 portRadius=2000 ; +PORT port_oil2 portRadius=2000 ; +PORT port_water1 portRadius=2000 ; +PORT port_out_waste portRadius=2000 ; +PORT port_out_keep portRadius=2000 ; + +PORT port_oil3 portRadius=2000 ; +PORT port_oil4 portRadius=2000 ; +PORT port_water2 portRadius=2000 ; + +NOZZLE DROPLET GENERATOR nozzle_droplet_generator_1 orificeSize=150 orificeLength=375 + oilInputWidth=600 waterInputWidth=375 outputWidth=300 outputLength=5000 height=300 ; + +NOZZLE DROPLET GENERATOR nozzle_droplet_generator_2 orificeSize=150 orificeLength=375 + oilInputWidth=600 waterInputWidth=375 outputWidth=300 outputLength=5000 height=300 ; + +MIXER mixer_1 bendSpacing=600 numberOfBends=5 channelWidth=300 bendLength=2000 height=300; + +DROPLET SORTER droplet_sorter_1 + height=300 + inletWidth=300 + inletLength=4000 + inletLength=4000 + electrodeDistance=1000 + electrodeWidth=700 + electrodeLength=5000 + outletWidth=300 + angle=45 + wasteWidth=600 + outputLength=4000 + keepWidth=600 + pressureWidth=1000 + numberofDistributors=5 + channelDepth=300 + electrodeDepth=300 + pressureDepth=200; + +CHANNEL connection_1 from port_oil1 to nozzle_droplet_generator_1 1 channelWidth=300 ; +CHANNEL connection_2 from port_oil2 to nozzle_droplet_generator_1 3 channelWidth=300 ; +CHANNEL connection_3 from port_water1 to nozzle_droplet_generator_1 4 channelWidth=300 ; +CHANNEL connection_4 from port_water2 to nozzle_droplet_generator_2 4 channelWidth=300 ; + +CHANNEL connection_5 from nozzle_droplet_generator_1 2 to mixer_1 1 channelWidth=300 ; + +CHANNEL connection_10 from port_oil3 to nozzle_droplet_generator_2 1 channelWidth=300 ; +CHANNEL connection_11 from port_oil4 to nozzle_droplet_generator_2 3 channelWidth=300 ; +CHANNEL connection_13 from nozzle_droplet_generator_2 2 to mixer_1 1 channelWidth=300 ; + +CHANNEL connection_7 from mixer_1 2 to droplet_sorter_1 1 channelWidth=300 ; + +CHANNEL connection_8 from droplet_sorter_1 2 to port_out_waste channelWidth=300 ; +CHANNEL connection_9 from droplet_sorter_1 3 to port_out_keep channelWidth=300 ; + +END LAYER + diff --git a/tests/data/DropX/netlists/dx3_ref.mint b/tests/data/DropX/netlists/dx3_ref.mint new file mode 100644 index 0000000..52893d8 --- /dev/null +++ b/tests/data/DropX/netlists/dx3_ref.mint @@ -0,0 +1,65 @@ +DEVICE dx3_ref + +LAYER FLOW + +PORT port_oil1 portRadius=2000 ; +PORT port_oil2 portRadius=2000 ; +PORT port_water portRadius=2000 ; + +NOZZLE DROPLET GENERATOR nozzle_droplet_generator_1 orificeSize=150 orificeLength=375 + oilInputWidth=600 waterInputWidth=375 outputWidth=300 outputLength=5000 height=300 ; + +CHANNEL connection_1 from port_oil1 to nozzle_droplet_generator_1 1 channelWidth=300 ; +CHANNEL connection_2 from port_oil2 to nozzle_droplet_generator_1 3 channelWidth=300 ; +CHANNEL connection_3 from port_water to nozzle_droplet_generator_1 4 channelWidth=300 ; + +DROPLET SPLITTER droplet_splitter_1 + height=30 + inletWidth=300 + inletLength=2000 + outletWidth1=300 + outletLength1=2000 + outletWidth2=300 + outletLength2=2000; + +PORT port_injector1 portRadius=2000 ; +PORT port_injector2 portRadius=2000 ; + +PICOINJECTOR picoinjector_1 height=300 + injectorWidth=1000 + width=10000 + injectorWidth=1000 + injectorLength=5000 + dropletWidth=100 + nozzleWidth=50 + nozzleLength=500 + electrodeDistance=500 + electrodeWidth=800 + electrodeLength=3000; + +PICOINJECTOR picoinjector_2 height=300 + injectorWidth=1000 + width=10000 + injectorWidth=1000 + injectorLength=5000 + dropletWidth=100 + nozzleWidth=50 + nozzleLength=500 + electrodeDistance=500 + electrodeWidth=800 + electrodeLength=3000; + +CHANNEL connection_4 from nozzle_droplet_generator_1 2 to droplet_splitter_1 1 channelWidth=300 ; +CHANNEL connection_5 from droplet_splitter_1 2 to picoinjector_1 1 channelWidth=300 ; +CHANNEL connection_6 from droplet_splitter_1 3 to picoinjector_2 1 channelWidth=300 ; +CHANNEL connection_7 from port_injector1 to picoinjector_1 3 channelWidth=300 ; +CHANNEL connection_8 from port_injector2 to picoinjector_2 3 channelWidth=300 ; + +PORT port_out1 portRadius=2000 ; +PORT port_out2 portRadius=2000 ; + +CHANNEL connection_9 from picoinjector_1 2 to port_out1 1 channelWidth=300 ; +CHANNEL connection_10 from picoinjector_2 2 to port_out2 1 channelWidth=300 ; + +END LAYER + diff --git a/tests/data/DropX/netlists/dx4_ref.mint b/tests/data/DropX/netlists/dx4_ref.mint new file mode 100644 index 0000000..84fb3d9 --- /dev/null +++ b/tests/data/DropX/netlists/dx4_ref.mint @@ -0,0 +1,131 @@ +DEVICE dx4_ref + +LAYER FLOW + +PORT port_oil1 portRadius=2000; +PORT port_oil2 portRadius=2000; +PORT port_water portRadius=2000; + +PORT port_injector1 portRadius=2000; +PORT port_injector2 portRadius=2000; +PORT port_injector3 portRadius=2000; +PORT port_injector4 portRadius=2000; + +MIXER mix_1 + bendSpacing=600 + numberOfBends=5 + channelWidth=300 + bendLength=2000 + height=300; + +CHANNEL connection_4 from port_injector1 to mix_1 1 channelWidth=50; +CHANNEL connection_5 from port_injector2 to mix_1 1 channelWidth=50; +CHANNEL connection_6 from port_injector3 to mix_1 1 channelWidth=50; +CHANNEL connection_7 from port_injector4 to mix_1 1 channelWidth=50; + +NOZZLE DROPLET GENERATOR nozzle_droplet_generator_1 + orificeSize=150 + orificeLength=375 + oilInputWidth=600 + waterInputWidth=375 + outputWidth=300 + outputLength=5000 + height=300; + +CHANNEL connection_1 from port_oil1 to nozzle_droplet_generator_1 1 channelWidth=300; +CHANNEL connection_2 from port_oil2 to nozzle_droplet_generator_1 3 channelWidth=300; +CHANNEL connection_3 from port_water to nozzle_droplet_generator_1 4 channelWidth=300; + +PICOINJECTOR pico_injector_1 + height=300 + injectorWidth=1000 + width=10000 + injectorWidth=1000 + injectorLength=5000 + dropletWidth=100 + nozzleWidth=50 + nozzleLength=500 + electrodeDistance=500 + electrodeWidth=800 + electrodeLength=3000; + +CHANNEL connection_8 from nozzle_droplet_generator_1 2 to pico_injector_1 1 channelWidth=300; +CHANNEL connection_9 from mix_1 2 to pico_injector_1 3 channelWidth=50; + +DROPLET SORTER droplet_sorter_1 + height=300 + inletWidth=300 + inletLength=4000 + inletLength=4000 + electrodeDistance=1000 + electrodeWidth=700 + electrodeLength=5000 + outletWidth=300 + angle=45 + wasteWidth=600 + outputLength=4000 + keepWidth=600 + pressureWidth=1000 + numberofDistributors=5 + channelDepth=300 + electrodeDepth=300 + pressureDepth=200; + +CHANNEL connection_10 from pico_injector_1 2 to droplet_sorter_1 1 channelWidth=300; + +DROPLET SORTER droplet_sorter_2 + height=300 + inletWidth=300 + inletLength=4000 + inletLength=4000 + electrodeDistance=1000 + electrodeWidth=700 + electrodeLength=5000 + outletWidth=300 + angle=45 + wasteWidth=600 + outputLength=4000 + keepWidth=600 + pressureWidth=1000 + numberofDistributors=5 + channelDepth=300 + electrodeDepth=300 + pressureDepth=200; + +DROPLET SORTER droplet_sorter_3 + height=300 + inletWidth=300 + inletLength=4000 + inletLength=4000 + electrodeDistance=1000 + electrodeWidth=700 + electrodeLength=5000 + outletWidth=300 + angle=45 + wasteWidth=600 + outputLength=4000 + keepWidth=600 + pressureWidth=1000 + numberofDistributors=5 + channelDepth=300 + electrodeDepth=300 + pressureDepth=200; + + +CHANNEL connection_11 from droplet_sorter_1 2 to droplet_sorter_2 1 channelWidth=300; +CHANNEL connection_12 from droplet_sorter_1 3 to droplet_sorter_3 1 channelWidth=300; + +PORT port_out_waste1 portRadius=2000; +PORT port_out_waste2 portRadius=2000; + +PORT port_out_keep1 portRadius=2000; +PORT port_out_keep2 portRadius=2000; + +CHANNEL connection_13 from droplet_sorter_2 2 to port_out_waste1 channelWidth=300; +CHANNEL connection_14 from droplet_sorter_3 2 to port_out_waste2 channelWidth=300; + +CHANNEL connection_15 from droplet_sorter_2 3 to port_out_keep1 channelWidth=300; +CHANNEL connection_16 from droplet_sorter_3 3 to port_out_keep2 channelWidth=300; + +END LAYER + diff --git a/tests/data/DropX/netlists/dx5_ref.mint b/tests/data/DropX/netlists/dx5_ref.mint new file mode 100644 index 0000000..1f5a997 --- /dev/null +++ b/tests/data/DropX/netlists/dx5_ref.mint @@ -0,0 +1,112 @@ +DEVICE dx5_ref + +LAYER FLOW + +PORT port_oil1 portRadius=2000; +PORT port_oil2 portRadius=2000; +PORT port_water portRadius=2000; + +NOZZLE DROPLET GENERATOR nozzle_droplet_generator_1 + orificeSize=150 + orificeLength=375 + oilInputWidth=600 + waterInputWidth=375 + outputWidth=300 + outputLength=5000 + height=300; + +CHANNEL connection_1 from port_oil1 to nozzle_droplet_generator_1 1 channelWidth=300; +CHANNEL connection_2 from port_oil2 to nozzle_droplet_generator_1 3 channelWidth=300; +CHANNEL connection_3 from port_water to nozzle_droplet_generator_1 4 channelWidth=300; + +PICOINJECTOR picoinjector_1 + height=300 + injectorWidth=1000 + width=10000 + injectorWidth=1000 + injectorLength=5000 + dropletWidth=100 + nozzleWidth=50 + nozzleLength=500 + electrodeDistance=500 + electrodeWidth=800 + electrodeLength=3000 +; + +CHANNEL connection_4 from nozzle_droplet_generator_1 2 to picoinjector_1 1 channelWidth=300; + +PORT port_injection portRadius=2000; + +CHANNEL connection_5 from port_injection to picoinjector_1 3 channelWidth=300; + +DROPLET SPLITTER droplet_splitter_1 + height=30 + inletWidth=300 + inletLength=2000 + outletWidth1=300 + outletLength1=2000 + outletWidth2=300 + outletLength2=2000; + +CHANNEL connection_6 from picoinjector_1 2 to droplet_splitter_1 1 channelWidth=300; + +MIXER mixer_1; +MIXER mixer_2; + +CHANNEL connection_7 from droplet_splitter_1 2 to mixer_1 1 channelWidth=300; +CHANNEL connection_8 from droplet_splitter_1 3 to mixer_2 1 channelWidth=300; + +DROPLET SORTER droplet_sorter_1 + height=300 + inletWidth=300 + inletLength=4000 + inletLength=4000 + electrodeDistance=1000 + electrodeWidth=700 + electrodeLength=5000 + outletWidth=300 + angle=45 + wasteWidth=600 + outputLength=4000 + keepWidth=600 + pressureWidth=1000 + numberofDistributors=5 + channelDepth=300 + electrodeDepth=300 + pressureDepth=200; + + +DROPLET SORTER droplet_sorter_2 + height=300 + inletWidth=300 + inletLength=4000 + inletLength=4000 + electrodeDistance=1000 + electrodeWidth=700 + electrodeLength=5000 + outletWidth=300 + angle=45 + wasteWidth=600 + outputLength=4000 + keepWidth=600 + pressureWidth=1000 + numberofDistributors=5 + channelDepth=300 + electrodeDepth=300 + pressureDepth=200; + +CHANNEL connection_9 from mixer_1 2 to droplet_sorter_1 1 channelWidth=300; +CHANNEL connection_10 from mixer_2 2 to droplet_sorter_2 1 channelWidth=300; + +PORT port_out_waste1 portRadius=2000; +PORT port_out_waste2 portRadius=2000; +PORT port_out_keep1 portRadius=2000; +PORT port_out_keep2 portRadius=2000; + +CHANNEL connection_11 from droplet_sorter_1 2 to port_out_waste1 1 channelWidth=300; +CHANNEL connection_12 from droplet_sorter_2 2 to port_out_waste2 1 channelWidth=300; +CHANNEL connection_13 from droplet_sorter_1 3 to port_out_keep1 1 channelWidth=300; +CHANNEL connection_14 from droplet_sorter_2 3 to port_out_keep2 1 channelWidth=300; + +END LAYER + diff --git a/tests/data/DropX/netlists/dx6_ref.mint b/tests/data/DropX/netlists/dx6_ref.mint new file mode 100644 index 0000000..e574ef2 --- /dev/null +++ b/tests/data/DropX/netlists/dx6_ref.mint @@ -0,0 +1,51 @@ +DEVICE dx6_ref + +LAYER FLOW + +PORT port_in1 portRadius=2000; +PORT port_in2 portRadius=2000; +MIXER mixer_1 + bendSpacing=600 + numberOfBends=5 + channelWidth=300 + bendLength=2000 + height=300; + +CHANNEL connection_1 from port_in1 to mixer_1 1 channelWidth=300; +CHANNEL connection_2 from port_in2 to mixer_1 1 channelWidth=300; + +PORT port_oil1 portRadius=2000; +PORT port_oil2 portRadius=2000; + +NOZZLE DROPLET GENERATOR nozzle_droplet_generator_1 + orificeSize=150 + orificeLength=375 + oilInputWidth=600 + waterInputWidth=375 + outputWidth=300 + outputLength=5000 + height=300; + +CHANNEL connection_3 from port_oil1 to nozzle_droplet_generator_1 1 channelWidth=300; +CHANNEL connection_4 from port_oil2 to nozzle_droplet_generator_1 3 channelWidth=300; +CHANNEL connection_5 from mixer_1 2 to nozzle_droplet_generator_1 4 channelWidth=300; + +DROPLET SPLITTER droplet_splitter_1 + height=30 + inletWidth=300 + inletLength=2000 + outletWidth1=300 + outletLength1=2000 + outletWidth2=300 + outletLength2=2000; + +CHANNEL connection_6 from nozzle_droplet_generator_1 2 to droplet_splitter_1 1 channelWidth=300; + +PORT port_out1 portRadius=2000; +PORT port_out2 portRadius=2000; + +CHANNEL connection_7 from droplet_splitter_1 2 to port_out1 channelWidth=300; +CHANNEL connection_8 from droplet_splitter_1 3 to port_out2 channelWidth=300; + +END LAYER + diff --git a/tests/data/DropX/netlists/dx7_ref.mint b/tests/data/DropX/netlists/dx7_ref.mint new file mode 100644 index 0000000..ad2d825 --- /dev/null +++ b/tests/data/DropX/netlists/dx7_ref.mint @@ -0,0 +1,113 @@ +DEVICE dx7_ref + +LAYER FLOW + +PORT port_oil1 portRadius=2000; +PORT port_oil2 portRadius=2000; +PORT port_water1 portRadius=2000; + +NOZZLE DROPLET GENERATOR nozzle_droplet_generator_1 + orificeSize=150 + orificeLength=375 + oilInputWidth=600 + waterInputWidth=375 + outputWidth=300 + outputLength=5000 + height=300; + +CHANNEL connection_1 from port_oil1 to nozzle_droplet_generator_1 1 channelWidth=300; +CHANNEL connection_2 from port_oil2 to nozzle_droplet_generator_1 3 channelWidth=300; +CHANNEL connection_3 from port_water1 to nozzle_droplet_generator_1 4 channelWidth=300; + +PORT port_oil3 portRadius=2000; +PORT port_oil4 portRadius=2000; +PORT port_water2 portRadius=2000; +NOZZLE DROPLET GENERATOR nozzle_droplet_generator_2 + orificeSize=150 + orificeLength=375 + oilInputWidth=600 + waterInputWidth=375 + outputWidth=300 + outputLength=5000 + height=300; + +CHANNEL connection_4 from port_oil3 to nozzle_droplet_generator_2 1 channelWidth=300; +CHANNEL connection_5 from port_oil4 to nozzle_droplet_generator_2 3 channelWidth=300; +CHANNEL connection_6 from port_water2 to nozzle_droplet_generator_2 4 channelWidth=300; + +DROPLET MERGER droplet_merger_1 + height=200 + inletWidth=300 + inletLength=4000 + electrodeWidth=1000 + electrodeLength=5000 + electrodeDistance=800 + outletWidth=300 + outletLength=4000 + chamberHeight=800 + chamberLength=800 + channelDepth=300 + electrodeDepth=200; + +CHANNEL connection_7 from nozzle_droplet_generator_1 2 to droplet_merger_1 1 channelWidth=300; +CHANNEL connection_8 from nozzle_droplet_generator_2 2 to droplet_merger_1 1 channelWidth=300; + +PORT port_injector1 portRadius=2000; +PORT port_injector2 portRadius=2000; + +MIXER mixer_1 + bendSpacing=600 + numberOfBends=5 + channelWidth=300 + bendLength=2000 + height=300; + +CHANNEL connection_9 from port_injector1 to mixer_1 1 channelWidth=300; +CHANNEL connection_10 from port_injector2 to mixer_1 1 channelWidth=300; + +PICOINJECTOR picoinjector_1 + height=300 + injectorWidth=1000 + width=10000 + injectorWidth=1000 + injectorLength=5000 + dropletWidth=100 + nozzleWidth=50 + nozzleLength=500 + electrodeDistance=500 + electrodeWidth=800 + electrodeLength=3000; + +CHANNEL connection_11 from mixer_1 2 to picoinjector_1 3 channelWidth=300; + +DROPLET SORTER droplet_sorter_1 + height=300 + inletWidth=300 + inletLength=4000 + inletLength=4000 + electrodeDistance=1000 + electrodeWidth=700 + electrodeLength=5000 + outletWidth=300 + angle=45 + wasteWidth=600 + outputLength=4000 + keepWidth=600 + pressureWidth=1000 + numberofDistributors=5 + channelDepth=300 + electrodeDepth=300 + pressureDepth=200; + +CHANNEL connection_12 from picoinjector_1 2 to droplet_sorter_1 1 channelWidth=300; + +PORT port_out_waste portRadius=2000; +PORT port_out_keep portRadius=2000; + +CHANNEL connection_13 from droplet_sorter_1 2 to port_out_waste channelWidth=300; +CHANNEL connection_14 from droplet_sorter_1 3 to port_out_keep channelWidth=300; + +CHANNEL connection_15 from droplet_merger_1 2 to picoinjector_1 1 channelWidth=300; + +END LAYER + diff --git a/tests/data/DropX/netlists/dx8_ref.mint b/tests/data/DropX/netlists/dx8_ref.mint new file mode 100644 index 0000000..8e62379 --- /dev/null +++ b/tests/data/DropX/netlists/dx8_ref.mint @@ -0,0 +1,136 @@ +DEVICE dx8_ref + +LAYER FLOW + +PORT port_oil1 portRadius=2000; +PORT port_oil2 portRadius=2000; + +PORT port_in1 portRadius=2000; +PORT port_in2 portRadius=2000; +PORT port_in3 portRadius=2000; + +NOZZLE DROPLET GENERATOR nozzle_droplet_generator_1 + orificeSize=150 + orificeLength=375 + oilInputWidth=600 + waterInputWidth=375 + outputWidth=300 + outputLength=5000 + height=300; + +CHANNEL connection_1 from port_in1 to nozzle_droplet_generator_1 4 channelWidth=300; +CHANNEL connection_2 from port_in2 to nozzle_droplet_generator_1 4 channelWidth=300; +CHANNEL connection_3 from port_in3 to nozzle_droplet_generator_1 4 channelWidth=300; + +CHANNEL connection_4 from port_oil1 to nozzle_droplet_generator_1 1 channelWidth=300; +CHANNEL connection_5 from port_oil2 to nozzle_droplet_generator_1 3 channelWidth=300; + +PICOINJECTOR pico_injector_1 height=300 + injectorWidth=1000 + width=10000 + injectorWidth=1000 + injectorLength=5000 + dropletWidth=100 + nozzleWidth=50 + nozzleLength=500 + electrodeDistance=500 + electrodeWidth=800 + electrodeLength=3000; + +PICOINJECTOR pico_injector_2 height=300 + injectorWidth=1000 + width=10000 + injectorWidth=1000 + injectorLength=5000 + dropletWidth=100 + nozzleWidth=50 + nozzleLength=500 + electrodeDistance=500 + electrodeWidth=800 + electrodeLength=3000; + +PICOINJECTOR pico_injector_3 height=300 + injectorWidth=1000 + width=10000 + injectorWidth=1000 + injectorLength=5000 + dropletWidth=100 + nozzleWidth=50 + nozzleLength=500 + electrodeDistance=500 + electrodeWidth=800 + electrodeLength=3000; + + +CHANNEL connection_6 from nozzle_droplet_generator_1 2 to pico_injector_1 1 channelWidth=300; +CHANNEL connection_7 from pico_injector_1 2 to pico_injector_2 1 channelWidth=300; +CHANNEL connection_8 from pico_injector_2 2 to pico_injector_3 1 channelWidth=300; + +PORT port_injector1 portRadius=2000; +PORT port_injector2 portRadius=2000; +PORT port_injector3 portRadius=2000; + +CHANNEL connection_9 from port_injector1 to pico_injector_1 3 channelWidth=300; +CHANNEL connection_10 from port_injector2 to pico_injector_2 3 channelWidth=300; +CHANNEL connection_11 from port_injector3 to pico_injector_3 3 channelWidth=300; + +MIXER mixer_1 + bendSpacing=600 + numberOfBends=5 + channelWidth=300 + bendLength=2000 + height=300; + +CHANNEL connection_12 from pico_injector_3 2 to mixer_1 1 channelWidth=300; + +DROPLET SORTER droplet_sorter_1 + height=300 + inletWidth=300 + inletLength=4000 + inletLength=4000 + electrodeDistance=1000 + electrodeWidth=700 + electrodeLength=5000 + outletWidth=300 + angle=45 + wasteWidth=600 + outputLength=4000 + keepWidth=600 + pressureWidth=1000 + numberofDistributors=5 + channelDepth=300 + electrodeDepth=300 + pressureDepth=200; + +DROPLET SORTER droplet_sorter_2 + height=300 + inletWidth=300 + inletLength=4000 + inletLength=4000 + electrodeDistance=1000 + electrodeWidth=700 + electrodeLength=5000 + outletWidth=300 + angle=45 + wasteWidth=600 + outputLength=4000 + keepWidth=600 + pressureWidth=1000 + numberofDistributors=5 + channelDepth=300 + electrodeDepth=300 + pressureDepth=200; + + +CHANNEL connection_13 from mixer_1 2 to droplet_sorter_1 1 channelWidth=300; + +PORT port_out1 portRadius=2000; +PORT port_out2 portRadius=2000; +PORT port_out3 portRadius=2000; + +CHANNEL connection_14 from droplet_sorter_1 3 to port_out1 channelWidth=300; +CHANNEL connection_15 from droplet_sorter_2 3 to port_out2 channelWidth=300; +CHANNEL connection_16 from droplet_sorter_1 2 to droplet_sorter_2 1 channelWidth=300; + +END LAYER + diff --git a/tests/data/DropX/netlists/dx9_ref.mint b/tests/data/DropX/netlists/dx9_ref.mint new file mode 100644 index 0000000..e87b4ec --- /dev/null +++ b/tests/data/DropX/netlists/dx9_ref.mint @@ -0,0 +1,101 @@ +DEVICE dx9_ref + +LAYER FLOW + +PORT port_oil1 portRadius=2000; +PORT port_oil2 portRadius=2000; +PORT port_water1 portRadius=2000; + +NOZZLE DROPLET GENERATOR nozzle_droplet_generator_1 + orificeSize=150 + orificeLength=375 + oilInputWidth=600 + waterInputWidth=375 + outputWidth=300 + outputLength=5000 + height=300; + +CHANNEL connection_1 from port_oil1 to nozzle_droplet_generator_1 1 channelWidth=300; +CHANNEL connection_2 from port_oil2 to nozzle_droplet_generator_1 3 channelWidth=300; +CHANNEL connection_3 from port_water1 to nozzle_droplet_generator_1 4 channelWidth=300; + +PORT port_oil3 portRadius=2000; +PORT port_oil4 portRadius=2000; +PORT port_water2 portRadius=2000; + +NOZZLE DROPLET GENERATOR nozzle_droplet_generator_2 + orificeSize=150 + orificeLength=375 + oilInputWidth=600 + waterInputWidth=375 + outputWidth=300 + outputLength=5000 + height=300; + +CHANNEL connection_4 from port_oil3 to nozzle_droplet_generator_2 1 channelWidth=300; +CHANNEL connection_5 from port_oil4 to nozzle_droplet_generator_2 3 channelWidth=300; +CHANNEL connection_6 from port_water2 to nozzle_droplet_generator_2 4 channelWidth=300; + +DROPLET MERGER droplet_merger_1 + height=200 + inletWidth=300 + inletLength=4000 + electrodeWidth=1000 + electrodeLength=5000 + electrodeDistance=800 + outletWidth=300 + outletLength=4000 + chamberHeight=800 + chamberLength=800 + channelDepth=300 + electrodeDepth=200; + +CHANNEL connection_7 from nozzle_droplet_generator_1 2 to droplet_merger_1 1 channelWidth=300; +CHANNEL connection_8 from nozzle_droplet_generator_2 2 to droplet_merger_1 1 channelWidth=300; + +PORT port_injector portRadius=2000; +PICOINJECTOR pico_injector_1 + height=300 + injectorWidth=1000 + width=10000 + injectorWidth=1000 + injectorLength=5000 + dropletWidth=100 + nozzleWidth=50 + nozzleLength=500 + electrodeDistance=500 + electrodeWidth=800 + electrodeLength=3000; + +CHANNEL connection_9 from port_injector to pico_injector_1 3 channelWidth=300; +CHANNEL connection_10 from droplet_merger_1 2 to pico_injector_1 1 channelWidth=300; + +DROPLET SORTER droplet_sorter_1 + height=300 + inletWidth=300 + inletLength=4000 + inletLength=4000 + electrodeDistance=1000 + electrodeWidth=700 + electrodeLength=5000 + outletWidth=300 + angle=45 + wasteWidth=600 + outputLength=4000 + keepWidth=600 + pressureWidth=1000 + numberofDistributors=5 + channelDepth=300 + electrodeDepth=300 + pressureDepth=200; + +CHANNEL connection_11 from pico_injector_1 2 to droplet_sorter_1 1 channelWidth=300; + +PORT port_outlet portRadius=2000; +PORT port_waste portRadius=2000; + +CHANNEL connection_12 from droplet_sorter_1 3 to port_outlet channelWidth=300; +CHANNEL connection_13 from droplet_sorter_1 2 to port_waste channelWidth=300; + +END LAYER + diff --git a/tests/data/Expressions/expression1.lfr b/tests/data/Expressions/expression1.lfr new file mode 100644 index 0000000..7454382 --- /dev/null +++ b/tests/data/Expressions/expression1.lfr @@ -0,0 +1,10 @@ +module expression1(finput in1, in2, foutput out); + + flow temp_out; + + assign temp_out = in1 % 50.88; + assign temp_out = in2 % 100; + + assign out = temp_out; + +endmodule diff --git a/tests/data/Expressions/expression10.lfr b/tests/data/Expressions/expression10.lfr new file mode 100644 index 0000000..2bcc759 --- /dev/null +++ b/tests/data/Expressions/expression10.lfr @@ -0,0 +1,11 @@ +module expression10(finput in1, in2, foutput out); + + flow temp_out; + number val1 = 9; + + val1 = 10 + 78 - 65 / 67 * 44; + assign temp_out = in1 % 50.88 + &in2; + + assign out = temp_out; + +endmodule \ No newline at end of file diff --git a/tests/data/Expressions/expression11.lfr b/tests/data/Expressions/expression11.lfr new file mode 100644 index 0000000..255ba83 --- /dev/null +++ b/tests/data/Expressions/expression11.lfr @@ -0,0 +1,9 @@ +module expression11(finput in1, in2, foutput out); + + flow temp_out; + + assign temp_out = in1 + in2; + + assign out = temp_out; + +endmodule diff --git a/tests/data/Expressions/expression12.lfr b/tests/data/Expressions/expression12.lfr new file mode 100644 index 0000000..f9ea7a2 --- /dev/null +++ b/tests/data/Expressions/expression12.lfr @@ -0,0 +1,9 @@ +module expression12(finput [0:7] in1, [0:7] in2, foutput out); + + flow temp_out; + + assign temp_out = in1 + in2; + + assign out = temp_out; + +endmodule diff --git a/tests/data/Expressions/expression13.lfr b/tests/data/Expressions/expression13.lfr new file mode 100644 index 0000000..ed58546 --- /dev/null +++ b/tests/data/Expressions/expression13.lfr @@ -0,0 +1,11 @@ +//This design tests the DIVIDE interaction +module expression13(finput in1, foutput out1, out2); + + flow temp_1, temp_2; + + assign {temp_1, temp_2} = in1/2; + + assign out1 = temp_1; + assign out2 = temp_2; + +endmodule \ No newline at end of file diff --git a/tests/data/Expressions/expression14.lfr b/tests/data/Expressions/expression14.lfr new file mode 100644 index 0000000..36894b0 --- /dev/null +++ b/tests/data/Expressions/expression14.lfr @@ -0,0 +1,6 @@ +//This design tests if N fluid number operations work +module expression14(finput [0:7] in, foutput [0:7] out); + + assign out = in%50; + +endmodule \ No newline at end of file diff --git a/tests/data/Expressions/expression15.lfr b/tests/data/Expressions/expression15.lfr new file mode 100644 index 0000000..6251419 --- /dev/null +++ b/tests/data/Expressions/expression15.lfr @@ -0,0 +1,9 @@ +module expression15( + finput f1, f2, f3, f4, f5, f6, + foutput fout +); + + assign fout = f1*0.2 + f2*0.4 + f3*0.1 + f4*0.1 + f5*0.1 + f6*0.1; + + +endmodule \ No newline at end of file diff --git a/tests/data/Expressions/expression2.lfr b/tests/data/Expressions/expression2.lfr new file mode 100644 index 0000000..04f6aeb --- /dev/null +++ b/tests/data/Expressions/expression2.lfr @@ -0,0 +1,9 @@ +module expression2(finput in1, in2, foutput out); + + flow temp_out; + + assign temp_out = in1 + in2 % 50; + + assign out = temp_out; + +endmodule diff --git a/tests/data/Expressions/expression3.lfr b/tests/data/Expressions/expression3.lfr new file mode 100644 index 0000000..eaad913 --- /dev/null +++ b/tests/data/Expressions/expression3.lfr @@ -0,0 +1,9 @@ +module expression3(finput in1, in2, in3, foutput out); + + flow temp_out; + + assign temp_out = &(in1 + in2 + in3) ; + + assign out = temp_out; + +endmodule diff --git a/tests/data/Expressions/expression4.lfr b/tests/data/Expressions/expression4.lfr new file mode 100644 index 0000000..55774a3 --- /dev/null +++ b/tests/data/Expressions/expression4.lfr @@ -0,0 +1,9 @@ +module expression4(finput in1, in2, in3, in4, foutput out); + + flow temp_out; + + assign temp_out = in1 + (in2 + (in3 + in4)) ; + + assign out = temp_out; + +endmodule diff --git a/tests/data/Expressions/expression5.lfr b/tests/data/Expressions/expression5.lfr new file mode 100644 index 0000000..4761500 --- /dev/null +++ b/tests/data/Expressions/expression5.lfr @@ -0,0 +1,9 @@ +module expression5(finput in1, in2, in3, in4, foutput out); + + flow temp_out; + + assign temp_out = in1 + (in2 + (in3 + &in4)) ; + + assign out = temp_out; + +endmodule diff --git a/tests/data/Expressions/expression6.lfr b/tests/data/Expressions/expression6.lfr new file mode 100644 index 0000000..ce67362 --- /dev/null +++ b/tests/data/Expressions/expression6.lfr @@ -0,0 +1,10 @@ +module expression6(finput in1, in2, in3, in4, foo, foutput out); + + flow temp_out; + + assign temp_out = foo + (in1 + (in2 + &(in3 + in4))) ; + + assign out = temp_out; + +endmodule + diff --git a/tests/data/Expressions/expression7.lfr b/tests/data/Expressions/expression7.lfr new file mode 100644 index 0000000..c85434d --- /dev/null +++ b/tests/data/Expressions/expression7.lfr @@ -0,0 +1,10 @@ +module expression7(finput [0:2] in1, in2, in3, in4, foutput out); + + flow temp_out; + + assign temp_out = in1 + { in2, in3, in4 } ; + + assign out = temp_out; + +endmodule + diff --git a/tests/data/Expressions/expression8.lfr b/tests/data/Expressions/expression8.lfr new file mode 100644 index 0000000..992984a --- /dev/null +++ b/tests/data/Expressions/expression8.lfr @@ -0,0 +1,11 @@ +module expression8(finput in1, in2, in3, in4, foutput out1, out2); + + flow temp_out1, temp_out2; + + assign {temp_out1, temp_out2} = {in1, in1, in1} + { in2, in3, in4 } ; + + assign out1 = temp_out1; + assign out2 = temp_out2; + +endmodule + diff --git a/tests/data/Expressions/expression9.lfr b/tests/data/Expressions/expression9.lfr new file mode 100644 index 0000000..328172b --- /dev/null +++ b/tests/data/Expressions/expression9.lfr @@ -0,0 +1,9 @@ +module expression9(finput in1, in2, foutput out); + + flow temp_out; + + assign temp_out = in1 % 50.88 + &in2; + + assign out = temp_out; + +endmodule \ No newline at end of file diff --git a/tests/pylfr/e2e/__init__.py b/tests/pylfr/e2e/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/pylfr/e2e/compiler/__init__.py b/tests/pylfr/e2e/compiler/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/pylfr/e2e/compiler/test_devicegen.py b/tests/pylfr/e2e/compiler/test_devicegen.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/pylfr/e2e/compiler/test_expressions.py b/tests/pylfr/e2e/compiler/test_expressions.py new file mode 100644 index 0000000..5a8f273 --- /dev/null +++ b/tests/pylfr/e2e/compiler/test_expressions.py @@ -0,0 +1,215 @@ +from tests.conftest import LIBRARY_PATH, TEST_DATA_FOLDER, TEST_OUTPATH + +from lfr import api + +TEST_CASES_FOLDER = TEST_DATA_FOLDER.joinpath("Expressions") + + +def test_expression1(): + # expression1.lfr + test_file = TEST_CASES_FOLDER.joinpath("expression1.lfr") + # Read the text and dump it on console + api.compile_lfr( + input_files=[str(test_file.absolute())], + outpath=str(TEST_OUTPATH), + library_path=str(LIBRARY_PATH), + no_gen_flag=True, + no_annotations_flag=True, + pre_load=[], + ) + + +def test_expression2(): + # expression2.lfr + test_file = TEST_CASES_FOLDER.joinpath("expression2.lfr") + # Read the text and dump it on console + api.compile_lfr( + input_files=[str(test_file.absolute())], + outpath=str(TEST_OUTPATH), + library_path=str(LIBRARY_PATH), + no_gen_flag=True, + no_annotations_flag=True, + pre_load=[], + ) + + +def test_expression3(): + # expression3.lfr + test_file = TEST_CASES_FOLDER.joinpath("expression3.lfr") + # Read the text and dump it on console + api.compile_lfr( + input_files=[str(test_file.absolute())], + outpath=str(TEST_OUTPATH), + library_path=str(LIBRARY_PATH), + no_gen_flag=True, + no_annotations_flag=True, + pre_load=[], + ) + + +def test_expression4(): + # expression4.lfr + test_file = TEST_CASES_FOLDER.joinpath("expression4.lfr") + # Read the text and dump it on console + api.compile_lfr( + input_files=[str(test_file.absolute())], + outpath=str(TEST_OUTPATH), + library_path=str(LIBRARY_PATH), + no_gen_flag=True, + no_annotations_flag=True, + pre_load=[], + ) + + +def test_expression5(): + # expression5.lfr + test_file = TEST_CASES_FOLDER.joinpath("expression5.lfr") + # Read the text and dump it on console + api.compile_lfr( + input_files=[str(test_file.absolute())], + outpath=str(TEST_OUTPATH), + library_path=str(LIBRARY_PATH), + no_gen_flag=True, + no_annotations_flag=True, + pre_load=[], + ) + + +def test_expression6(): + # expression6.lfr + test_file = TEST_CASES_FOLDER.joinpath("expression6.lfr") + # Read the text and dump it on console + api.compile_lfr( + input_files=[str(test_file.absolute())], + outpath=str(TEST_OUTPATH), + library_path=str(LIBRARY_PATH), + no_gen_flag=True, + no_annotations_flag=True, + pre_load=[], + ) + + +def test_expression7(): + # expression7.lfr + test_file = TEST_CASES_FOLDER.joinpath("expression7.lfr") + # Read the text and dump it on console + api.compile_lfr( + input_files=[str(test_file.absolute())], + outpath=str(TEST_OUTPATH), + library_path=str(LIBRARY_PATH), + no_gen_flag=True, + no_annotations_flag=True, + pre_load=[], + ) + + +def test_expression8(): + # expression8.lfr + test_file = TEST_CASES_FOLDER.joinpath("expression8.lfr") + # Read the text and dump it on console + api.compile_lfr( + input_files=[str(test_file.absolute())], + outpath=str(TEST_OUTPATH), + library_path=str(LIBRARY_PATH), + no_gen_flag=True, + no_annotations_flag=True, + pre_load=[], + ) + + +def test_expression9(): + # expression9.lfr + test_file = TEST_CASES_FOLDER.joinpath("expression9.lfr") + # Read the text and dump it on console + api.compile_lfr( + input_files=[str(test_file.absolute())], + outpath=str(TEST_OUTPATH), + library_path=str(LIBRARY_PATH), + no_gen_flag=True, + no_annotations_flag=True, + pre_load=[], + ) + + +def test_expression10(): + # expression10.lfr + test_file = TEST_CASES_FOLDER.joinpath("expression10.lfr") + # Read the text and dump it on console + api.compile_lfr( + input_files=[str(test_file.absolute())], + outpath=str(TEST_OUTPATH), + library_path=str(LIBRARY_PATH), + no_gen_flag=True, + no_annotations_flag=True, + pre_load=[], + ) + + +def test_expression11(): + # expression11.lfr + test_file = TEST_CASES_FOLDER.joinpath("expression11.lfr") + # Read the text and dump it on console + api.compile_lfr( + input_files=[str(test_file.absolute())], + outpath=str(TEST_OUTPATH), + library_path=str(LIBRARY_PATH), + no_gen_flag=True, + no_annotations_flag=True, + pre_load=[], + ) + + +def test_expression12(): + # expression12.lfr + test_file = TEST_CASES_FOLDER.joinpath("expression12.lfr") + # Read the text and dump it on console + api.compile_lfr( + input_files=[str(test_file.absolute())], + outpath=str(TEST_OUTPATH), + library_path=str(LIBRARY_PATH), + no_gen_flag=True, + no_annotations_flag=True, + pre_load=[], + ) + + +def test_expression13(): + # expression13.lfr + test_file = TEST_CASES_FOLDER.joinpath("expression13.lfr") + # Read the text and dump it on console + api.compile_lfr( + input_files=[str(test_file.absolute())], + outpath=str(TEST_OUTPATH), + library_path=str(LIBRARY_PATH), + no_gen_flag=True, + no_annotations_flag=True, + pre_load=[], + ) + + +def test_expression14(): + # expression14.lfr + test_file = TEST_CASES_FOLDER.joinpath("expression14.lfr") + # Read the text and dump it on console + api.compile_lfr( + input_files=[str(test_file.absolute())], + outpath=str(TEST_OUTPATH), + library_path=str(LIBRARY_PATH), + no_gen_flag=True, + no_annotations_flag=True, + pre_load=[], + ) + + +def test_expression15(): + # expression15.lfr + test_file = TEST_CASES_FOLDER.joinpath("expression15.lfr") + # Read the text and dump it on console + api.compile_lfr( + input_files=[str(test_file.absolute())], + outpath=str(TEST_OUTPATH), + library_path=str(LIBRARY_PATH), + no_gen_flag=True, + no_annotations_flag=True, + pre_load=[], + ) diff --git a/tests/pylfr/e2e/compiler/test_figgen.py b/tests/pylfr/e2e/compiler/test_figgen.py new file mode 100644 index 0000000..30584b2 --- /dev/null +++ b/tests/pylfr/e2e/compiler/test_figgen.py @@ -0,0 +1,400 @@ +from pathlib import Path + +import networkx +from networkx import is_isomorphic +from tests.conftest import LIBRARY_PATH, TEST_DATA_FOLDER, TEST_OUTPATH + +from lfr import api +from lfr.utils import printgraph + +TEST_CASES_FOLDER = TEST_DATA_FOLDER.joinpath("DropX") +REF_DATA_FIG_FOLDER = TEST_CASES_FOLDER.joinpath("figs") + + +def test_dx1(): + # dx1.lfr + test_file = TEST_CASES_FOLDER.joinpath("dx1.lfr") + # Read the text and dump it on console + module_listener = api.synthesize_module( + input_path=test_file, + no_annotations_flag=True, + print_fig=False, + ) + + assert module_listener.success is True + assert module_listener.currentModule is not None + + # Get FIG from the module + fig = module_listener.currentModule.FIG + # Load the reference data + ref_fig_data = REF_DATA_FIG_FOLDER.joinpath("dx1.dot") + ref_graph = networkx.nx_agraph.read_dot(ref_fig_data) + # Compare the reference data with the generated data and assert equality + # Ref data load the dot file using networkx + # Generated data load the dot file using pydot + success = is_isomorphic(ref_graph, fig) + assert success is True + + +def test_dx2(): + # dx2.lfr + test_file = TEST_CASES_FOLDER.joinpath("dx2.lfr") + # Read the text and dump it on console + module_listener = api.synthesize_module( + input_path=test_file, + no_annotations_flag=True, + print_fig=False, + ) + + assert module_listener.success is True + assert module_listener.currentModule is not None + + # Get FIG from the module + fig = module_listener.currentModule.FIG + + # Load the reference data + ref_fig_data = REF_DATA_FIG_FOLDER.joinpath("dx2.dot") + ref_graph = networkx.nx_agraph.read_dot(ref_fig_data) + # Compare the reference data with the generated data and assert equality + # Ref data load the dot file using networkx + # Generated data load the dot file using pydot + success = is_isomorphic(ref_graph, fig) + assert success is True + + +def test_dx3(): + # dx3.lfr + test_file = TEST_CASES_FOLDER.joinpath("dx3.lfr") + # Read the text and dump it on console + module_listener = api.synthesize_module( + input_path=test_file, + no_annotations_flag=True, + print_fig=False, + ) + + assert module_listener.success is True + assert module_listener.currentModule is not None + + # Get FIG from the module + fig = module_listener.currentModule.FIG + + # Load the reference data + ref_fig_data = REF_DATA_FIG_FOLDER.joinpath("dx3.dot") + ref_graph = networkx.nx_agraph.read_dot(ref_fig_data) + # Compare the reference data with the generated data and assert equality + # Ref data load the dot file using networkx + # Generated data load the dot file using pydot + success = is_isomorphic(ref_graph, fig) + assert success is True + + +def test_dx4(): + # dx4.lfr + test_file = TEST_CASES_FOLDER.joinpath("dx4.lfr") + # Read the text and dump it on console + module_listener = api.synthesize_module( + input_path=test_file, + no_annotations_flag=True, + print_fig=False, + ) + + assert module_listener.success is True + assert module_listener.currentModule is not None + + # Get FIG from the module + fig = module_listener.currentModule.FIG + + # Load the reference data + ref_fig_data = REF_DATA_FIG_FOLDER.joinpath("dx4.dot") + ref_graph = networkx.nx_agraph.read_dot(ref_fig_data) + # Compare the reference data with the generated data and assert equality + # Ref data load the dot file using networkx + # Generated data load the dot file using pydot + success = is_isomorphic(ref_graph, fig) + assert success is True + + +def test_dx5(): + # dx5.lfr + test_file = TEST_CASES_FOLDER.joinpath("dx5.lfr") + # Read the text and dump it on console + module_listener = api.synthesize_module( + input_path=test_file, + no_annotations_flag=True, + print_fig=False, + ) + + assert module_listener.success is True + assert module_listener.currentModule is not None + + # Get FIG from the module + fig = module_listener.currentModule.FIG + + # Load the reference data + ref_fig_data = REF_DATA_FIG_FOLDER.joinpath("dx5.dot") + ref_graph = networkx.nx_agraph.read_dot(ref_fig_data) + # Compare the reference data with the generated data and assert equality + # Ref data load the dot file using networkx + # Generated data load the dot file using pydot + success = is_isomorphic(ref_graph, fig) + assert success is True + + +def test_dx6(): + # dx6.lfr + test_file = TEST_CASES_FOLDER.joinpath("dx6.lfr") + # Read the text and dump it on console + module_listener = api.synthesize_module( + input_path=test_file, + no_annotations_flag=True, + print_fig=False, + ) + + assert module_listener.success is True + assert module_listener.currentModule is not None + + # Get FIG from the module + fig = module_listener.currentModule.FIG + + # Load the reference data + ref_fig_data = REF_DATA_FIG_FOLDER.joinpath("dx6.dot") + ref_graph = networkx.nx_agraph.read_dot(ref_fig_data) + # Compare the reference data with the generated data and assert equality + # Ref data load the dot file using networkx + # Generated data load the dot file using pydot + success = is_isomorphic(ref_graph, fig) + assert success is True + + +def test_dx7(): + # dx7.lfr + test_file = TEST_CASES_FOLDER.joinpath("dx7.lfr") + # Read the text and dump it on console + module_listener = api.synthesize_module( + input_path=test_file, + no_annotations_flag=True, + print_fig=False, + ) + + assert module_listener.success is True + assert module_listener.currentModule is not None + + # Get FIG from the module + fig = module_listener.currentModule.FIG + + # Load the reference data + ref_fig_data = REF_DATA_FIG_FOLDER.joinpath("dx7.dot") + ref_graph = networkx.nx_agraph.read_dot(ref_fig_data) + # Compare the reference data with the generated data and assert equality + # Ref data load the dot file using networkx + # Generated data load the dot file using pydot + success = is_isomorphic(ref_graph, fig) + assert success is True + + +def test_dx8(): + # dx8.lfr + test_file = TEST_CASES_FOLDER.joinpath("dx8.lfr") + # Read the text and dump it on console + module_listener = api.synthesize_module( + input_path=test_file, + no_annotations_flag=True, + print_fig=False, + ) + + assert module_listener.success is True + assert module_listener.currentModule is not None + + # Get FIG from the module + fig = module_listener.currentModule.FIG + + # Load the reference data + ref_fig_data = REF_DATA_FIG_FOLDER.joinpath("dx8.dot") + ref_graph = networkx.nx_agraph.read_dot(ref_fig_data) + # Compare the reference data with the generated data and assert equality + # Ref data load the dot file using networkx + # Generated data load the dot file using pydot + success = is_isomorphic(ref_graph, fig) + assert success is True + + +def test_dx9(): + # dx9.lfr + test_file = TEST_CASES_FOLDER.joinpath("dx9.lfr") + # Read the text and dump it on console + module_listener = api.synthesize_module( + input_path=test_file, + no_annotations_flag=True, + print_fig=False, + ) + + assert module_listener.success is True + assert module_listener.currentModule is not None + + # Get FIG from the module + fig = module_listener.currentModule.FIG + + # Load the reference data + ref_fig_data = REF_DATA_FIG_FOLDER.joinpath("dx9.dot") + ref_graph = networkx.nx_agraph.read_dot(ref_fig_data) + # Compare the reference data with the generated data and assert equality + # Ref data load the dot file using networkx + # Generated data load the dot file using pydot + success = is_isomorphic(ref_graph, fig) + assert success is True + + +def test_dx10(): + # dx10.lfr + test_file = TEST_CASES_FOLDER.joinpath("dx10.lfr") + # Read the text and dump it on console + module_listener = api.synthesize_module( + input_path=test_file, + no_annotations_flag=True, + print_fig=False, + ) + + assert module_listener.success is True + assert module_listener.currentModule is not None + + # Get FIG from the module + fig = module_listener.currentModule.FIG + + # Load the reference data + ref_fig_data = REF_DATA_FIG_FOLDER.joinpath("dx10.dot") + ref_graph = networkx.nx_agraph.read_dot(ref_fig_data) + # Compare the reference data with the generated data and assert equality + # Ref data load the dot file using networkx + # Generated data load the dot file using pydot + success = is_isomorphic(ref_graph, fig) + assert success is True + + +def test_dx11(): + # dx11.lfr + test_file = TEST_CASES_FOLDER.joinpath("dx11.lfr") + # Read the text and dump it on console + module_listener = api.synthesize_module( + input_path=test_file, + no_annotations_flag=True, + print_fig=False, + ) + + assert module_listener.success is True + assert module_listener.currentModule is not None + + # Get FIG from the module + fig = module_listener.currentModule.FIG + + # Load the reference data + ref_fig_data = REF_DATA_FIG_FOLDER.joinpath("dx11.dot") + ref_graph = networkx.nx_agraph.read_dot(ref_fig_data) + # Compare the reference data with the generated data and assert equality + # Ref data load the dot file using networkx + # Generated data load the dot file using pydot + success = is_isomorphic(ref_graph, fig) + assert success is True + + +def test_dx12(): + # dx12.lfr + test_file = TEST_CASES_FOLDER.joinpath("dx12.lfr") + # Read the text and dump it on console + module_listener = api.synthesize_module( + input_path=test_file, + no_annotations_flag=True, + print_fig=False, + ) + + assert module_listener.success is True + assert module_listener.currentModule is not None + + # Get FIG from the module + fig = module_listener.currentModule.FIG + + # Load the reference data + ref_fig_data = REF_DATA_FIG_FOLDER.joinpath("dx12.dot") + ref_graph = networkx.nx_agraph.read_dot(ref_fig_data) + # Compare the reference data with the generated data and assert equality + # Ref data load the dot file using networkx + # Generated data load the dot file using pydot + success = is_isomorphic(ref_graph, fig) + assert success is True + + +def test_dx13(): + # dx13.lfr + test_file = TEST_CASES_FOLDER.joinpath("dx13.lfr") + # Read the text and dump it on console + module_listener = api.synthesize_module( + input_path=test_file, + no_annotations_flag=True, + print_fig=False, + ) + + assert module_listener.success is True + assert module_listener.currentModule is not None + + # Get FIG from the module + fig = module_listener.currentModule.FIG + + # Load the reference data + ref_fig_data = REF_DATA_FIG_FOLDER.joinpath("dx13.dot") + ref_graph = networkx.nx_agraph.read_dot(ref_fig_data) + # Compare the reference data with the generated data and assert equality + # Ref data load the dot file using networkx + # Generated data load the dot file using pydot + success = is_isomorphic(ref_graph, fig) + assert success is True + + +def test_dx14(): + # dx14.lfr + test_file = TEST_CASES_FOLDER.joinpath("dx14.lfr") + # Read the text and dump it on console + module_listener = api.synthesize_module( + input_path=test_file, + no_annotations_flag=True, + print_fig=False, + ) + + assert module_listener.success is True + assert module_listener.currentModule is not None + + # Get FIG from the module + fig = module_listener.currentModule.FIG + + # Load the reference data + ref_fig_data = REF_DATA_FIG_FOLDER.joinpath("dx14.dot") + ref_graph = networkx.nx_agraph.read_dot(ref_fig_data) + # Compare the reference data with the generated data and assert equality + # Ref data load the dot file using networkx + # Generated data load the dot file using pydot + success = is_isomorphic(ref_graph, fig) + assert success is True + + +def test_dx15(): + # dx15.lfr + test_file = TEST_CASES_FOLDER.joinpath("dx15.lfr") + # Read the text and dump it on console + module_listener = api.synthesize_module( + input_path=test_file, + no_annotations_flag=True, + print_fig=False, + ) + + assert module_listener.success is True + assert module_listener.currentModule is not None + + # Get FIG from the module + fig = module_listener.currentModule.FIG + + # Load the reference data + ref_fig_data = REF_DATA_FIG_FOLDER.joinpath("dx15.dot") + ref_graph = networkx.nx_agraph.read_dot(ref_fig_data) + # Compare the reference data with the generated data and assert equality + # Ref data load the dot file using networkx + # Generated data load the dot file using pydot + success = is_isomorphic(ref_graph, fig) + assert success is True diff --git a/tests/pylfr/e2e/compiler/test_mintgen.py b/tests/pylfr/e2e/compiler/test_mintgen.py new file mode 100644 index 0000000..ae6f9f8 --- /dev/null +++ b/tests/pylfr/e2e/compiler/test_mintgen.py @@ -0,0 +1,32 @@ +from pathlib import Path + +import networkx +from networkx import is_isomorphic +from tests.conftest import LIBRARY_PATH, TEST_DATA_FOLDER, TEST_OUTPATH + +from lfr import api +from lfr.utils import printgraph + +TEST_CASES_FOLDER = TEST_DATA_FOLDER.joinpath("DropX") +REF_DATA_FIG_FOLDER = TEST_CASES_FOLDER.joinpath("netlists") + + +def test_dx1(): + # Create output directories + TEST_OUTPATH.mkdir(parents=True, exist_ok=True) + + # dx1.lfr + test_file = TEST_CASES_FOLDER.joinpath("dx1.lfr") + + # Run through the full compiler + return_code = api.compile_lfr( + input_files=[str(test_file)], + outpath=str(TEST_OUTPATH), + technology="dropx", + pre_load=[str(LIBRARY_PATH.absolute())], + ) + + assert return_code == 0 + + # Load the data from the output path + # Parse this and compare it with the reference data