コード例 #1
0
    def __setCriticalLinks(self, save=False, queries={}, crit_res_result=None):
        a = AequilibraeMatrix()
        if save:
            if crit_res_result is None:
                warnings.warn(
                    "Critical Link analysis not set properly. Need to specify output file too"
                )
            else:
                if crit_res_result[-3:].lower() != "aem":
                    crit_res_result += ".aes"

                if self.nodes > 0 and self.zones > 0:
                    if ["elements", "labels", "type"] in queries.keys():
                        if len(queries["labels"]) == len(
                                queries["elements"]) == len(queries["type"]):
                            a.create_empty(file_name=crit_res_result,
                                           zones=self.zones,
                                           matrix_names=queries["labels"])
                        else:
                            raise ValueError(
                                "Queries are inconsistent. 'Labels', 'elements' and 'type' need to have same dimensions"
                            )
                    else:
                        raise ValueError(
                            "Queries are inconsistent. It needs to contain the following elements: 'Labels', 'elements' and 'type'"
                        )
        else:
            a.create_empty(file_name=a.random_name(),
                           zones=self.zones,
                           matrix_names=["empty", "nothing"])

        a.computational_view()
        if len(a.matrix_view.shape[:]) == 2:
            a.matrix_view = a.matrix_view.reshape((self.zones, self.zones, 1))
        self.critical_links = {"save": save, "queries": queries, "results": a}
コード例 #2
0
    def test_set_pce(self):
        mat_name = AequilibraeMatrix().random_name()
        g = Graph()
        g.load_from_disk(test_graph)
        g.set_graph(cost_field="distance")

        # Creates the matrix for assignment
        args = {
            "file_name": os.path.join(gettempdir(), mat_name),
            "zones": g.num_zones,
            "matrix_names": ["cars", "trucks"],
            "index_names": ["my indices"],
        }

        matrix = AequilibraeMatrix()
        matrix.create_empty(**args)

        matrix.index[:] = g.centroids[:]
        matrix.cars.fill(1.1)
        matrix.trucks.fill(2.2)
        matrix.computational_view()

        tc = TrafficClass(graph=g, matrix=matrix)

        self.assertIsInstance(tc.results, AssignmentResults, 'Results have the wrong type')
        self.assertIsInstance(tc._aon_results, AssignmentResults, 'Results have the wrong type')

        with self.assertRaises(ValueError):
            tc.set_pce('not a number')
        tc.set_pce(1)
        tc.set_pce(3.9)
コード例 #3
0
    def test___init__(self):
        os.remove(name_test) if os.path.exists(name_test) else None
        args = {'file_name': name_test,
                'zones': zones,
                'matrix_names': ['mat', 'seed', 'dist'],
                'index_names': ['my indices']}

        matrix = AequilibraeMatrix()
        matrix.create_empty(**args)

        matrix.index[:] = np.arange(matrix.zones) + 100
        matrix.mat[:, :] = np.random.rand(matrix.zones, matrix.zones)[:, :]
        matrix.mat[:, :] = matrix.mat[:, :] * (1000 / np.sum(matrix.mat[:, :]))
        matrix.setName('Test matrix - ' + str(random.randint(1, 10)))
        matrix.setDescription('Generated at ' + datetime.datetime.now().strftime("%I:%M%p on %B %d, %Y"))
        matrix.close(True)
        del (matrix)
コード例 #4
0
    def test_execute(self):
        # Loads and prepares the graph
        g = Graph()
        g.load_from_disk(test_graph)
        g.set_graph(cost_field="distance", skim_fields=None)
        # None implies that only the cost field will be skimmed

        # Prepares the matrix for assignment
        args = {
            "file_name": os.path.join(gettempdir(), "my_matrix.aem"),
            "zones": g.num_zones,
            "matrix_names": ["cars", "trucks"],
            "index_names": ["my indices"],
        }

        matrix = AequilibraeMatrix()
        matrix.create_empty(**args)

        matrix.index[:] = g.centroids[:]
        matrix.cars.fill(1)
        matrix.trucks.fill(2)
        matrix.computational_view(["cars"])

        # Performs assignment
        res = AssignmentResults()
        res.prepare(g, matrix)

        assig = allOrNothing(matrix, g, res)
        assig.execute()

        res.save_to_disk(os.path.join(gettempdir(), "link_loads.aed"))
        res.save_to_disk(os.path.join(gettempdir(), "link_loads.csv"))

        matrix.computational_view()
        # Performs assignment
        res = AssignmentResults()
        res.prepare(g, matrix)

        assig = allOrNothing(matrix, g, res)
        assig.execute()
        res.save_to_disk(os.path.join(gettempdir(), "link_loads_2_classes.aed"))
        res.save_to_disk(os.path.join(gettempdir(), "link_loads_2_classes.csv"))
コード例 #5
0
    def test_execute(self):
        # Loads and prepares the graph
        g = Graph()
        g.load_from_disk(test_graph)
        g.set_graph(cost_field='distance', skim_fields=None)
        # None implies that only the cost field will be skimmed

        # Prepares the matrix for assignment
        args = {
            'file_name': '/tmp/my_matrix.aem',
            'zones': g.num_zones,
            'matrix_names': ['cars', 'trucks'],
            'index_names': ['my indices']
        }

        matrix = AequilibraeMatrix()
        matrix.create_empty(**args)

        matrix.index[:] = g.centroids[:]
        matrix.cars.fill(1)
        matrix.trucks.fill(2)
        matrix.computational_view(['cars'])

        # Performs assignment
        res = AssignmentResults()
        res.prepare(g, matrix)

        assig = allOrNothing(matrix, g, res)
        assig.execute()

        res.save_to_disk('/tmp/link_loads.aed')
        res.save_to_disk('/tmp/link_loads.csv')

        matrix.computational_view()
        # Performs assignment
        res = AssignmentResults()
        res.prepare(g, matrix)

        assig = allOrNothing(matrix, g, res)
        assig.execute()
        res.save_to_disk('/tmp/link_loads_2_classes.aed')
        res.save_to_disk('/tmp/link_loads_2_classes.csv')
コード例 #6
0
    def test___init__(self):
        os.remove(name_test) if os.path.exists(name_test) else None
        args = {
            "file_name": name_test,
            "zones": zones,
            "matrix_names": ["mat", "seed", "dist"],
            "index_names": ["my indices"],
        }

        matrix = AequilibraeMatrix()
        matrix.create_empty(**args)

        matrix.index[:] = np.arange(matrix.zones) + 100
        matrix.mat[:, :] = np.random.rand(matrix.zones, matrix.zones)[:, :]
        matrix.mat[:, :] = matrix.mat[:, :] * (1000 / np.sum(matrix.mat[:, :]))
        matrix.setName("Test matrix - " + str(random.randint(1, 10)))
        matrix.setDescription(
            "Generated at " +
            datetime.datetime.now().strftime("%I:%M%p on %B %d, %Y"))
        matrix.close()
        del matrix
コード例 #7
0
    def test_execute(self):
        # Loads and prepares the graph
        g = Graph()
        g.load_from_disk(test_graph)
        g.set_graph(cost_field='distance', skim_fields=None)
        # None implies that only the cost field will be skimmed

        # Prepares the matrix for assignment
        args = {'file_name': os.path.join(gettempdir(),'my_matrix.aem'),
                'zones': g.num_zones,
                'matrix_names': ['cars', 'trucks'],
                'index_names': ['my indices']}

        matrix = AequilibraeMatrix()
        matrix.create_empty(**args)

        matrix.index[:] = g.centroids[:]
        matrix.cars.fill(1)
        matrix.trucks.fill(2)
        matrix.computational_view(['cars'])

        # Performs assignment
        res = AssignmentResults()
        res.prepare(g, matrix)

        assig = allOrNothing(matrix, g, res)
        assig.execute()

        res.save_to_disk(os.path.join(gettempdir(),'link_loads.aed'))
        res.save_to_disk(os.path.join(gettempdir(),'link_loads.csv'))

        matrix.computational_view()
        # Performs assignment
        res = AssignmentResults()
        res.prepare(g, matrix)

        assig = allOrNothing(matrix, g, res)
        assig.execute()
        res.save_to_disk(os.path.join(gettempdir(),'link_loads_2_classes.aed'))
        res.save_to_disk(os.path.join(gettempdir(),'link_loads_2_classes.csv'))
コード例 #8
0
    def setUp(self) -> None:
        self.mat_name = AequilibraeMatrix().random_name()
        self.g = Graph()
        self.g.load_from_disk(test_graph)
        self.g.set_graph(cost_field="distance")

        # Creates the matrix for assignment
        args = {
            "file_name": os.path.join(gettempdir(), self.mat_name),
            "zones": self.g.num_zones,
            "matrix_names": ["cars", "trucks"],
            "index_names": ["my indices"],
        }

        matrix = AequilibraeMatrix()
        matrix.create_empty(**args)

        matrix.index[:] = self.g.centroids[:]
        matrix.cars.fill(1.1)
        matrix.trucks.fill(2.2)

        # Exports matrix to OMX in order to have two matrices to work with
        matrix.export(os.path.join(gettempdir(), "my_matrix.omx"))
        matrix.close()
コード例 #9
0
class AssignmentResults:
    """
    Assignment result holder for a single :obj:`TrafficClass` with multiple user classes
    """
    def __init__(self):
        self.link_loads = None  # type: np.array  # The actual results for assignment
        self.total_link_loads = None  # type: np.array  # The result of the assignment for all user classes summed
        self.skims = None  # The array of skims
        self.no_path = None  # The list os paths
        self.num_skims = None  # number of skims that will be computed. Depends on the setting of the graph provided
        p = Parameters().parameters['system']['cpus']
        if not isinstance(p, int):
            p = 0
        self.set_cores(p)

        self.classes = {"number": 1, "names": ["flow"]}

        self.critical_links = {
            "save": False,
            "queries": {},
            "results": False
        }  # Queries are a dictionary

        self.link_extraction = {
            "save": False,
            "queries": {},
            "output": None
        }  # Queries are a dictionary

        self.path_file = {"save": False, "results": None}

        self.nodes = -1
        self.zones = -1
        self.links = -1
        self.__graph_id__ = None
        self.__float_type = None
        self.__integer_type = None

        self.lids = None
        self.direcs = None

    # In case we want to do by hand, we can prepare each method individually
    def prepare(self, graph: Graph, matrix: AequilibraeMatrix) -> None:
        """
        Prepares the object with dimensions corresponding to the assignment matrix and graph objects

        Args:
            *graph* (:obj:`Graph`): Needs to have been set with number of centroids and list of skims (if any)

            *matrix* (:obj:`AequilibraeMatrix`): Matrix properly set for computation with
             matrix.computational_view(:obj:`list`)
        """

        self.__float_type = graph.default_types("float")
        self.__integer_type = graph.default_types("int")

        if matrix.view_names is None:
            raise ("Please set the matrix_procedures computational view")
        else:
            self.classes["number"] = 1
            if len(matrix.matrix_view.shape) > 2:
                self.classes["number"] = matrix.matrix_view.shape[2]
            self.classes["names"] = matrix.view_names

        if graph is None:
            raise ("Please provide a graph")
        else:
            self.nodes = graph.num_nodes
            self.zones = graph.num_zones
            self.centroids = graph.centroids
            self.links = graph.num_links
            self.num_skims = len(graph.skim_fields)
            self.skim_names = [x for x in graph.skim_fields]
            self.lids = graph.graph["link_id"]
            self.direcs = graph.graph["direction"]
            self.__redim()
            self.__graph_id__ = graph.__id__

            # TODO: Enable these methods when the work for select link analysis and saving path files is completed
            self.__setSavePathFile(False)
            self.__setCriticalLinks(False)

    def reset(self) -> None:
        """
        Resets object to prepared and pre-computation state
        """
        if self.num_skims > 0:
            self.skims.matrices.fill(0)
        if self.link_loads is not None:
            self.no_path.fill(0)
            self.link_loads.fill(0)
            self.total_link_loads.fill(0)
        else:
            raise ValueError(
                "Exception: Assignment results object was not yet prepared/initialized"
            )

    def __redim(self):
        self.link_loads = np.zeros((self.links, self.classes["number"]),
                                   self.__float_type)
        self.total_link_loads = np.zeros(self.links, self.__float_type)
        self.no_path = np.zeros((self.zones, self.zones),
                                dtype=self.__integer_type)

        if self.num_skims > 0:
            self.skims = AequilibraeMatrix()

            self.skims.create_empty(file_name=self.skims.random_name(),
                                    zones=self.zones,
                                    matrix_names=self.skim_names)
            self.skims.index[:] = self.centroids[:]
            self.skims.computational_view()
            if len(self.skims.matrix_view.shape[:]) == 2:
                self.skims.matrix_view = self.skims.matrix_view.reshape(
                    (self.zones, self.zones, 1))
        else:
            self.skims = AequilibraeMatrix()
            self.skims.matrix_view = np.array((1, 1, 1))

        self.reset()

    def total_flows(self) -> None:
        """ Totals all link flows for this class into a single link load

        Results are placed into *total_link_loads* class member
        """
        sum_axis1(self.total_link_loads, self.link_loads, self.cores)

    def set_cores(self, cores: int) -> None:
        """
        Sets number of cores (threads) to be used in computation

        Value of zero sets number of threads to all available in the system, while negative values indicate the number
        of threads to be left out of the computational effort.

        Resulting number of cores will be adjusted to a minimum of zero or the maximum available in the system if the
        inputs result in values outside those limits

        Args:
            *cores* (:obj:`int`): Number of cores to be used in computation
        """

        if isinstance(cores, int):
            if cores < 0:
                self.cores = max(1, mp.cpu_count() + cores)
            if cores == 0:
                self.cores = mp.cpu_count()
            elif cores > 0:
                cores = max(mp.cpu_count(), cores)
                if self.cores != cores:
                    self.cores = cores
                    if self.link_loads is not None:
                        self.__redim()
        else:
            raise ValueError("Number of cores needs to be an integer")

    def __setCriticalLinks(self, save=False, queries={}, crit_res_result=None):
        a = AequilibraeMatrix()
        if save:
            if crit_res_result is None:
                warnings.warn(
                    "Critical Link analysis not set properly. Need to specify output file too"
                )
            else:
                if crit_res_result[-3:].lower() != "aem":
                    crit_res_result += ".aes"

                if self.nodes > 0 and self.zones > 0:
                    if ["elements", "labels", "type"] in queries.keys():
                        if len(queries["labels"]) == len(
                                queries["elements"]) == len(queries["type"]):
                            a.create_empty(file_name=crit_res_result,
                                           zones=self.zones,
                                           matrix_names=queries["labels"])
                        else:
                            raise ValueError(
                                "Queries are inconsistent. 'Labels', 'elements' and 'type' need to have same dimensions"
                            )
                    else:
                        raise ValueError(
                            "Queries are inconsistent. It needs to contain the following elements: 'Labels', 'elements' and 'type'"
                        )
        else:
            a.create_empty(file_name=a.random_name(),
                           zones=self.zones,
                           matrix_names=["empty", "nothing"])

        a.computational_view()
        if len(a.matrix_view.shape[:]) == 2:
            a.matrix_view = a.matrix_view.reshape((self.zones, self.zones, 1))
        self.critical_links = {"save": save, "queries": queries, "results": a}

    def __setSavePathFile(self, save=False, path_result=None):
        # Fields: Origin, Node, Predecessor
        # Number of records: Origins * Nodes
        a = AequilibraeData()
        d1 = max(1, self.zones)
        d2 = 1
        memory_mode = True

        if save:
            if path_result is None:
                warnings.warn(
                    "Path file not set properly. Need to specify output file too"
                )
            else:
                # This is the only place where we keep 32bits, as going 64 bits would explode the file size
                if self.nodes > 0 and self.zones > 0:
                    d1 = self.zones
                    d2 = self.nodes
                    memory_mode = False

        a.create_empty(
            file_path=path_result,
            entries=d1 * d2,
            field_names=["origin", "node", "predecessor", "connector"],
            data_types=[np.uint32, np.uint32, np.uint32, np.uint32],
            memory_mode=memory_mode,
        )

        self.path_file = {"save": save, "results": a}

    def get_load_results(self) -> AequilibraeData:
        """
        Translates the assignment results from the graph format into the network format

        Returns:
            dataset (:obj:`AequilibraeData`): AequilibraE data with the traffic class assignment results
        """
        fields = ['link_id']
        for n in self.classes['names']:
            fields.extend([f'{n}_ab', f'{n}_ba', f'{n}_tot'])
        types = [np.float64] * len(fields)

        entries = int(np.unique(self.lids).shape[0])
        res = AequilibraeData()
        res.create_empty(memory_mode=True,
                         entries=entries,
                         field_names=fields,
                         data_types=types)
        res.data.fill(np.nan)
        res.index[:] = np.unique(self.lids)[:]
        res.link_id[:] = res.index[:]

        indexing = np.zeros(int(self.lids.max()) + 1, np.uint64)
        indexing[res.index[:]] = np.arange(entries)

        # Indices of links BA and AB
        ABs = self.direcs > 0
        BAs = self.direcs < 0
        ab_ids = indexing[self.lids[ABs]]
        ba_ids = indexing[self.lids[BAs]]

        # Link flows
        link_flows = self.link_loads[:, :]
        for i, n in enumerate(self.classes["names"]):
            # AB Flows
            res.data[n + "_ab"][ab_ids] = np.nan_to_num(link_flows[ABs, i])
            # BA Flows
            res.data[n + "_ba"][ba_ids] = np.nan_to_num(link_flows[BAs, i])

            # Tot Flow
            res.data[n + "_tot"] = np.nan_to_num(
                res.data[n + "_ab"]) + np.nan_to_num(res.data[n + "_ba"])
        return res

    def save_to_disk(self, file_name=None, output="loads") -> None:
        """ Function to write to disk all outputs computed during assignment

        Args:
            *file_name* (:obj:`str`): Name of the file, with extension. Valid extensions are: ['aed', 'csv', 'sqlite']
            *output* (:obj:`str`, optional): Type of output ('loads', 'path_file'). Defaults to 'loads'
        """

        if output == "loads":
            res = self.get_load_results()
            res.export(file_name)

        # TODO: Re-factor the exporting of the path file within the AequilibraeData format
        elif output == "path_file":
            pass
コード例 #10
0
class MatrixReblocking(WorkerThread):
    def __init__(self, parentThread, **kwargs):
        WorkerThread.__init__(self, parentThread)
        self.matrix = AequilibraeMatrix()
        self.matrices = kwargs.get('matrices')
        self.sparse = kwargs.get('sparse', False)
        self.file_name = kwargs.get('file_name',
                                    AequilibraeMatrix().random_name())

        self.num_matrices = len(self.matrices.keys())
        self.matrix_hash = {}
        self.titles = []
        self.report = []

    def doWork(self):
        if self.sparse:
            # Builds the hash
            self.ProgressMaxValue.emit(self.num_matrices)
            self.ProgressValue.emit(0)
            self.ProgressText.emit("Building correspondence")

            indices = None
            p = 0
            for mat_name, mat in self.matrices.items():
                # Gets all non-zero coordinates and makes sure that they are considered
                froms = mat['from']
                tos = mat['to']

                if indices is None:
                    all_indices = np.hstack((froms, tos))
                else:
                    all_indices = np.hstack((indices, froms, tos))
                indices = np.unique(all_indices)
                p += 1
                self.ProgressValue.emit(p)
            compact_shape = int(indices.shape[0])
        else:
            compact_shape = 0
            for mat_name, mat in self.matrices.items():
                compact_shape = np.max(compact_shape, mat.shape[0])
            indices = np.arange(compact_shape)

        new_index = {k: i for i, k in enumerate(indices)}

        names = [str(n) for n in self.matrices.keys()]
        self.matrix.create_empty(file_name=self.file_name,
                                 zones=compact_shape,
                                 matrix_names=names,
                                 data_type=np.float64)

        self.matrix.index[:] = indices[:]

        k = 0
        self.ProgressMaxValue.emit(self.num_matrices)
        self.ProgressText.emit("Reblocking matrices")

        new_mat = None
        for mat_name, mat in self.matrices.items():
            if self.sparse:
                new_mat = np.copy(mat)
                for j, v in new_index.items():
                    new_mat['from'][mat['from'] == j] = v
                    new_mat['to'][mat['to'] == j] = v
                k += 1
                self.ProgressValue.emit(k)
            else:
                k += 1
                self.ProgressValue.emit(1)

            # In order to differentiate the zeros from the NaNs in the future matrix
            if new_mat is None:
                raise ValueError('Could not create reblocked matrix.')
            new_mat['flow'][new_mat['flow'] == 0] = np.inf

            # Uses SciPy Sparse matrices to build the compact one
            self.matrix.matrix[mat_name][:, :] = coo_matrix(
                (new_mat['flow'], (new_mat['from'], new_mat['to'])),
                shape=(compact_shape,
                       compact_shape)).toarray().astype(np.float64)

            # In order to differentiate the zeros from the NaNs in the future matrix
            self.matrix.matrix[mat_name][self.matrix.matrix[mat_name] ==
                                         0] = np.nan
            self.matrix.matrix[mat_name][self.matrix.matrix[mat_name] ==
                                         np.inf] = 0

            del (mat)
            del (new_mat)

        self.ProgressText.emit("Matrix Reblocking finalized")
        self.finished_threaded_procedure.emit("REBLOCKED MATRICES")
コード例 #11
0
class TrafficAssignmentDialog(QtWidgets.QDialog, FORM_CLASS):
    def __init__(self, iface):
        QtWidgets.QDialog.__init__(self)
        self.iface = iface
        self.setupUi(self)
        self.path = standard_path()
        self.output_path = None
        self.temp_path = None
        self.error = None
        self.report = None
        self.method = {}
        self.matrices = OrderedDict()
        self.skims = []
        self.matrix = None
        self.graph = Graph()
        self.results = AssignmentResults()
        self.block_centroid_flows = None
        self.worker_thread = None

        # Signals for the matrix_procedures tab
        self.but_load_new_matrix.clicked.connect(self.find_matrices)

        # Signals from the Network tab
        self.load_graph_from_file.clicked.connect(self.load_graph)

        # Signals for the algorithm tab
        self.progressbar0.setVisible(False)
        self.progressbar0.setValue(0)
        self.progress_label0.setVisible(False)

        self.do_assignment.clicked.connect(self.run)
        self.cancel_all.clicked.connect(self.exit_procedure)
        self.select_output_folder.clicked.connect(self.choose_folder_for_outputs)

        self.cb_choose_algorithm.addItem('All-Or-Nothing')
        self.cb_choose_algorithm.currentIndexChanged.connect(self.changing_algorithm)

        # slots for skim tab
        self.but_build_query.clicked.connect(partial(self.build_query, 'select link'))

        self.changing_algorithm()

        # path file
        self.path_file = OutputType()

        # Queries
        tables = [self.select_link_list, self.list_link_extraction]
        for table in tables:
            table.setColumnWidth(0, 280)
            table.setColumnWidth(1, 40)
            table.setColumnWidth(2, 150)
            table.setColumnWidth(3, 40)

        self.graph_properties_table.setColumnWidth(0, 190)
        self.graph_properties_table.setColumnWidth(1, 240)

        # critical link
        self.but_build_query.clicked.connect(partial(self.build_query, 'select link'))
        self.do_select_link.stateChanged.connect(self.set_behavior_special_analysis)
        self.tot_crit_link_queries = 0
        self.critical_output = OutputType()

        # link flow extraction
        self.but_build_query_extract.clicked.connect(partial(self.build_query, 'Link flow extraction'))
        self.do_extract_link_flows.stateChanged.connect(self.set_behavior_special_analysis)
        self.tot_link_flow_extract = 0
        self.link_extract = OutputType()

        # Disabling resources not yet implemented
        self.do_select_link.setEnabled(False)
        self.but_build_query.setEnabled(False)
        self.select_link_list.setEnabled(False)
        self.skim_list_table.setEnabled(False)

        self.do_extract_link_flows.setEnabled(False)
        self.but_build_query_extract.setEnabled(False)
        self.list_link_extraction.setEnabled(False)
        self.new_matrix_to_assign()

        self.table_matrix_list.setColumnWidth(0, 135)
        self.table_matrix_list.setColumnWidth(1, 135)
        self.table_matrices_to_assign.setColumnWidth(0, 125)
        self.table_matrices_to_assign.setColumnWidth(1, 125)
        self.skim_list_table.setColumnWidth(0, 70)
        self.skim_list_table.setColumnWidth(1, 490)

    def choose_folder_for_outputs(self):
        new_name = GetOutputFolderName(self.path, 'Output folder for traffic assignment')
        if new_name:
            self.output_path = new_name
            self.lbl_output.setText(new_name)
        else:
            self.output_path = None
            self.lbl_output.setText(new_name)

    def load_graph(self):
        self.lbl_graphfile.setText('')

        file_types = ["AequilibraE graph(*.aeg)"]
        default_type = '.aeg'
        box_name = 'Traffic Assignment'
        graph_file, _ = GetOutputFileName(self, box_name, file_types, default_type, self.path)

        if graph_file is not None:
            self.graph.load_from_disk(graph_file)

            fields = list(set(self.graph.graph.dtype.names) - set(self.graph.required_default_fields))
            self.minimizing_field.addItems(fields)
            self.update_skim_list(fields)
            self.lbl_graphfile.setText(graph_file)

            cores = get_parameter_chain(['system', 'cpus'])
            self.results.set_cores(cores)

            # show graph properties
            def centers_item(qt_item):
                cell_widget = QWidget()
                lay_out = QHBoxLayout(cell_widget)
                lay_out.addWidget(qt_item)
                lay_out.setAlignment(Qt.AlignCenter)
                lay_out.setContentsMargins(0, 0, 0, 0)
                cell_widget.setLayout(lay_out)
                return cell_widget

            items = [['Graph ID', self.graph.__id__],
                     ['Number of links', self.graph.num_links],
                     ['Number of nodes', self.graph.num_nodes],
                     ['Number of centroids', self.graph.num_zones]]

            self.graph_properties_table.clearContents()
            self.graph_properties_table.setRowCount(5)
            for i, item in enumerate(items):
                self.graph_properties_table.setItem(i, 0, QTableWidgetItem(item[0]))
                self.graph_properties_table.setItem(i, 1, QTableWidgetItem(str(item[1])))

            self.graph_properties_table.setItem(4, 0, QTableWidgetItem('Block flows through centroids'))
            self.block_centroid_flows = QCheckBox()
            self.block_centroid_flows.setChecked(self.graph.block_centroid_flows)
            self.graph_properties_table.setCellWidget(4, 1, centers_item(self.block_centroid_flows))
        else:
            self.graph = Graph()
        self.set_behavior_special_analysis()

    def changing_algorithm(self):
        if self.cb_choose_algorithm.currentText() == 'All-Or-Nothing':
            self.method['algorithm'] = 'AoN'

    def run_thread(self):
        self.worker_thread.assignment.connect(self.signal_handler)
        # QObject.connect(self.worker_thread, SIGNAL("assignment"), self.signal_handler)
        self.worker_thread.start()
        self.exec_()

    def job_finished_from_thread(self):
        self.report = self.worker_thread.report
        self.produce_all_outputs()

        self.exit_procedure()

    def run(self):
        if self.check_data():
            self.set_output_names()
            self.progress_label0.setVisible(True)
            self.progressbar0.setVisible(True)
            self.progressbar0.setRange(0, self.graph.num_zones)
            try:
                if self.method['algorithm'] == 'AoN':
                    self.worker_thread = allOrNothing(self.matrix, self.graph, self.results)
                self.run_thread()
            except ValueError as error:
                qgis.utils.iface.messageBar().pushMessage("Input error", error.message, level=3)
        else:
            qgis.utils.iface.messageBar().pushMessage("Input error", self.error, level=3)

    def set_output_names(self):
        self.path_file.temp_file = os.path.join(self.temp_path, 'path_file.aed')
        self.path_file.output_name = os.path.join(self.output_path, 'path_file')
        self.path_file.extension = 'aed'

        if self.do_path_file.isChecked():
            self.results.setSavePathFile(save=True, path_result=self.path_file.temp_file)

        self.link_extract.temp_file = os.path.join(self.temp_path, 'link_extract')
        self.link_extract.output_name = os.path.join(self.output_path, 'link_extract')
        self.link_extract.extension = 'aed'

        self.critical_output.temp_file = os.path.join(self.temp_path, 'critical_output')
        self.critical_output.output_name = os.path.join(self.output_path, 'critical_output')
        self.critical_output.extension = 'aed'

    def check_data(self):
        self.error = None

        self.change_graph_settings()

        if not self.graph.num_links:
            self.error = 'Graph was not loaded'
            return False

        self.matrix = None
        if not self.prepare_assignable_matrices():
            return False

        if self.matrix is None:
            self.error = 'Demand matrix missing'
            return False

        if self.output_path is None:
            self.error = 'Parameters for output missing'
            return False

        self.temp_path = os.path.join(self.output_path, 'temp')
        if not os.path.exists(self.temp_path):
            os.makedirs(self.temp_path)

        self.results.prepare(self.graph, self.matrix)
        return True

    def load_assignment_queries(self):
        # First we load the assignment queries
        query_labels = []
        query_elements = []
        query_types = []
        if self.tot_crit_link_queries:
            for i in range(self.tot_crit_link_queries):
                links = eval(self.select_link_list.item(i, 0).text())
                query_type = self.select_link_list.item(i, 1).text()
                query_name = self.select_link_list.item(i, 2).text()

                for l in links:
                    d = directions_dictionary[l[1]]
                    lk = self.graph.ids[(self.graph.graph['link_id'] == int(l[0])) &
                                        (self.graph.graph['direction'] == d)]

                query_labels.append(query_name)
                query_elements.append(lk)
                query_types.append(query_type)

        self.critical_queries = {'labels': query_labels,
                                 'elements': query_elements,
                                 ' type': query_types}

    def signal_handler(self, val):
        if val[0] == 'zones finalized':
            self.progressbar0.setValue(val[1])
        elif val[0] == 'text AoN':
            self.progress_label0.setText(val[1])
        elif val[0] == 'finished_threaded_procedure':
            self.job_finished_from_thread()

    # TODO: Write code to export skims
    def produce_all_outputs(self):

        extension = 'aed'
        if not self.do_output_to_aequilibrae.isChecked():
            extension = 'csv'
            if self.do_output_to_sqlite.isChecked():
                extension = 'sqlite'

        # Save link flows to disk
        self.results.save_to_disk(os.path.join(self.output_path, 'link_flows.' + extension), output='loads')

        # save Path file if that is the case
        if self.do_path_file.isChecked():
            if self.method['algorithm'] == 'AoN':
                if self.do_output_to_sqlite.isChecked():
                    self.results.save_to_disk(file_name=os.path.join(self.output_path, 'path_file.' + extension),
                                              output='path_file')

        # Saves output skims
        if self.skim_list_table.rowCount() > 0:
            self.results.skims.copy(os.path.join(self.output_path, 'skims.aem'))

        # if self.do_select_link.isChecked():
        #     if self.method['algorithm'] == 'AoN':
        #         del(self.results.critical_links['results'])
        #         self.results.critical_links = None
        #
        #         shutil.move(self.critical_output.temp_file + '.aep', self.critical_output.output_name)
        #         shutil.move(self.critical_output.temp_file + '.aed', self.critical_output.output_name[:-3] + 'aed')
        #
        # if self.do_extract_link_flows.isChecked():
        #     if self.method['algorithm'] == 'AoN':
        #         del(self.results.link_extraction['results'])
        #         self.results.link_extraction = None
        #
        #         shutil.move(self.link_extract.temp_file + '.aep', self.link_extract.output_name)
        #         shutil.move(self.link_extract.temp_file + '.aed', self.link_extract.output_name[:-3] + 'aed')

    # Procedures related to critical analysis. Not yet fully implemented
    def build_query(self, purpose):
        if purpose == 'select link':
            button = self.but_build_query
            message = 'Select Link Analysis'
            table = self.select_link_list
            counter = self.tot_crit_link_queries
        else:
            button = self.but_build_query_extract
            message = 'Link flow extraction'
            table = self.list_link_extraction
            counter = self.tot_link_flow_extract

        button.setEnabled(False)
        dlg2 = LoadSelectLinkQueryBuilderDialog(self.iface, self.graph.graph, message)
        dlg2.exec_()

        if dlg2.links is not None:
            table.setRowCount(counter + 1)
            text = ''
            for i in dlg2.links:
                text = text + ', (' + only_str(i[0]) + ', "' + only_str(i[1]) + '")'
            text = text[2:]
            table.setItem(counter, 0, QTableWidgetItem(text))
            table.setItem(counter, 1, QTableWidgetItem(dlg2.query_type))
            table.setItem(counter, 2, QTableWidgetItem(dlg2.query_name))
            del_button = QPushButton('X')
            del_button.clicked.connect(partial(self.click_button_inside_the_list, purpose))
            table.setCellWidget(counter, 3, del_button)
            counter += 1

        if purpose == 'select link':
            self.tot_crit_link_queries = counter

        elif purpose == 'Link flow extraction':
            self.tot_link_flow_extract = counter

        button.setEnabled(True)

    def click_button_inside_the_list(self, purpose):
        if purpose == 'select link':
            table = self.select_link_list
        else:
            table = self.list_link_extraction

        button = self.sender()
        index = self.select_link_list.indexAt(button.pos())
        row = index.row()
        table.removeRow(row)

        if purpose == 'select link':
            self.tot_crit_link_queries -= 1
        elif purpose == 'Link flow extraction':
            self.tot_link_flow_extract -= 1

    def set_behavior_special_analysis(self):
        if self.graph.num_links < 1:
            behavior = False
        else:
            behavior = True

        self.do_path_file.setEnabled(behavior)

        # This line of code turns off the features of select link analysis and link flow extraction while these
        # features are still being developed
        behavior = False

        self.do_select_link.setEnabled(behavior)
        self.do_extract_link_flows.setEnabled(behavior)

        self.but_build_query.setEnabled(behavior * self.do_select_link.isChecked())
        self.select_link_list.setEnabled(behavior * self.do_select_link.isChecked())

        self.list_link_extraction.setEnabled(behavior * self.do_extract_link_flows.isChecked())
        self.but_build_query_extract.setEnabled(behavior * self.do_extract_link_flows.isChecked())

    def update_skim_list(self, skims):
        self.skim_list_table.clearContents()
        self.skim_list_table.setRowCount(len(skims))

        for i, skm in enumerate(skims):
            self.skim_list_table.setItem(i, 1, QTableWidgetItem(skm))
            chb = QCheckBox()
            my_widget = QWidget()
            lay_out = QHBoxLayout(my_widget)
            lay_out.addWidget(chb)
            lay_out.setAlignment(Qt.AlignCenter)
            lay_out.setContentsMargins(0, 0, 0, 0)
            my_widget.setLayout(lay_out)

            self.skim_list_table.setCellWidget(i, 0, my_widget)

    # All Matrix loading and assignables selection
    def update_matrix_list(self):
        self.table_matrix_list.clearContents()
        self.table_matrix_list.clearContents()
        self.table_matrix_list.setEditTriggers(QAbstractItemView.NoEditTriggers)
        self.table_matrix_list.setRowCount(len(self.matrices.keys()))

        for i, data_name in enumerate(self.matrices.keys()):
            self.table_matrix_list.setItem(i, 0, QTableWidgetItem(data_name))

            cbox = QComboBox()
            for idx in self.matrices[data_name].index_names:
                cbox.addItem(str(idx))
            self.table_matrix_list.setCellWidget(i, 1, cbox)

    def find_matrices(self):
        dlg2 = LoadMatrixDialog(self.iface)
        dlg2.show()
        dlg2.exec_()
        if dlg2.matrix is not None:
            matrix_name = dlg2.matrix.file_path
            matrix_name = os.path.splitext(os.path.basename(matrix_name))[0]
            matrix_name = self.find_non_conflicting_name(matrix_name, self.matrices)
            self.matrices[matrix_name] = dlg2.matrix
            self.update_matrix_list()

            row_count = self.table_matrices_to_assign.rowCount()
            new_matrix = list(self.matrices.keys())[-1]

            for i in range(row_count):
                cb = self.table_matrices_to_assign.cellWidget(i, 0)
                cb.insertItem(-1, new_matrix)

    def find_non_conflicting_name(self, data_name, dictio):
        if data_name in dictio:
            i = 1
            new_data_name = data_name + '_' + str(i)
            while new_data_name in dictio:
                i += 1
                new_data_name = data_name + '_' + str(i)
            data_name = new_data_name
        return data_name

    def changed_assignable_matrix(self, mi):
        chb = self.sender()
        mat_name = chb.currentText()

        table = self.table_matrices_to_assign
        for row in range(table.rowCount()):
            if table.cellWidget(row, 0) == chb:
                break

        if len(mat_name) == 0:
            if row + 1 < table.rowCount():
                self.table_matrices_to_assign.removeRow(row)
        else:
            mat_cores = self.matrices[mat_name].names
            cbox2 = QComboBox()
            cbox2.addItems(mat_cores)
            self.table_matrices_to_assign.setCellWidget(row, 1, cbox2)

            if row + 1 == table.rowCount():
                self.new_matrix_to_assign()

    def new_matrix_to_assign(self):
        # We edit ALL the combo boxes to have the current list of matrices
        row_count = self.table_matrices_to_assign.rowCount()
        self.table_matrices_to_assign.setRowCount(row_count + 1)

        cbox = QComboBox()
        cbox.addItems(list(self.matrices.keys()))
        cbox.addItem('')
        cbox.setCurrentIndex(cbox.count() - 1)
        cbox.currentIndexChanged.connect(self.changed_assignable_matrix)
        self.table_matrices_to_assign.setCellWidget(row_count, 0, cbox)

    def prepare_assignable_matrices(self):
        table = self.table_matrices_to_assign
        idx = self.graph.centroids
        mat_names = []
        if table.rowCount() > 1:
            for row in range(table.rowCount() - 1):
                mat = table.cellWidget(row, 0).currentText()
                core = table.cellWidget(row, 1).currentText()

                mat_index = self.matrices[mat].index
                if not np.array_equal(idx, mat_index):
                    no_zones = [item for item in mat_index if item not in idx]
                    # We only return an error if the matrix has too many centroids
                    if no_zones:
                        self.error = 'Assignable matrix has centroids that do not exist in the network: {}'.format(
                            ','.join([str(x) for x in no_zones]))
                        return False
                if core in mat_names:
                    self.error = 'Assignable matrices cannot have same names'
                    return False
                mat_names.append(only_str(core))

            self.matrix = AequilibraeMatrix()
            self.matrix.create_empty(file_name=self.matrix.random_name(),
                                     zones=idx.shape[0],
                                     matrix_names=mat_names)
            self.matrix.index[:] = idx[:]

            for row in range(table.rowCount() - 1):
                mat = table.cellWidget(row, 0).currentText()
                core = table.cellWidget(row, 1).currentText()
                src_mat = self.matrices[mat].matrix[core]
                dest_mat = self.matrix.matrix[core]

                rows = src_mat.shape[0]
                cols = src_mat.shape[1]
                dest_mat[:rows, :cols] = src_mat[:, :]

                # Inserts cols and rows that don;t exist
                if rows != self.matrix.zones:
                    src_index = list(self.matrices[mat].index[:])
                    for i, row in enumerate(idx):
                        if row not in src_index:
                            dest_mat[i + 1:, :] = dest_mat[i:-1, :]
                            dest_mat[i, :] = 0

                if cols != self.matrix.zones:
                    for j, col in enumerate(idx):
                        if col not in src_index:
                            dest_mat[:, j + 1:] = dest_mat[:, j:-1]
                            dest_mat[:, j] = 0

            self.matrix.computational_view()
        else:
            self.error = 'You need to have at least one matrix to assign'
            return False

        return True

    def change_graph_settings(self):
        skims = []
        table = self.skim_list_table
        for i in range(table.rowCount()):
            for chb in table.cellWidget(i, 0).findChildren(QCheckBox):
                if chb.isChecked():
                    skims.append(only_str(table.item(i, 1).text()))

        if len(skims) == 0:
            skims = False

        self.graph.set_graph(cost_field=self.minimizing_field.currentText(),
                             skim_fields=skims,
                             block_centroid_flows=self.block_centroid_flows.isChecked())

    def exit_procedure(self):
        self.close()
        if self.report:
            dlg2 = ReportDialog(self.iface, self.report)
            dlg2.show()
            dlg2.exec_()
コード例 #12
0
args["field_names"] = ["columns"]
column_vector = AequilibraeData()
column_vector.create_empty(**args)
column_vector.index[:] = np.arange(column_vector.entries) + 100
column_vector.columns[:] = column_vector.index[:] + np.random.rand(zones)[:]

# balance vectors
column_vector.columns[:] = column_vector.columns[:] * (row_vector.rows.sum() / column_vector.columns.sum())

# Impedance matrix_procedures
name_test = os.path.join(tempfile.gettempdir(), "aequilibrae_matrix_test.aem")

args = {"file_name": name_test, "zones": zones, "matrix_names": ["impedance"]}

matrix = AequilibraeMatrix()
matrix.create_empty(**args)

# randoms = np.random.randint(5, size=(2, 4))
matrix.impedance[:, :] = np.random.rand(zones, zones)[:, :]
matrix.index[:] = np.arange(matrix.zones) + 100
matrix.computational_view(["impedance"])

model_expo = SyntheticGravityModel()
model_expo.function = "EXPO"
model_expo.beta = 0.1

model_gamma = SyntheticGravityModel()
model_gamma.function = "GAMMA"
model_gamma.beta = 0.1
model_gamma.alpha = -0.2
コード例 #13
0
class TestAequilibraeMatrix(TestCase):
    matrix = None

    def setUp(self) -> None:
        self.sf_skims = f"/Aequilibrae_matrix_{uuid.uuid4()}.omx"
        copyfile(siouxfalls_skims, self.sf_skims)
        temp_folder = gettempdir()
        self.name_test = temp_folder + f"/Aequilibrae_matrix_{uuid.uuid4()}.aem"
        self.copy_matrix_name = temp_folder + f"/Aequilibrae_matrix_{uuid.uuid4()}.aem"
        self.csv_export_name = temp_folder + f"/Aequilibrae_matrix_{uuid.uuid4()}.csv"
        self.omx_export_name = temp_folder + f"/Aequilibrae_matrix_{uuid.uuid4()}.omx"

        if self.matrix is not None:
            return
        args = {
            "file_name": self.name_test,
            "zones": zones,
            "matrix_names": ["mat", "seed", "dist"],
            "index_names": ["my indices"],
        }

        self.matrix = AequilibraeMatrix()
        self.matrix.create_empty(**args)

        self.matrix.index[:] = np.arange(self.matrix.zones) + 100
        self.matrix.mat[:, :] = np.random.rand(self.matrix.zones, self.matrix.zones)[:, :]
        self.matrix.mat[:, :] = self.matrix.mat[:, :] * (1000 / np.sum(self.matrix.mat[:, :]))
        self.matrix.setName("Test matrix - " + str(random.randint(1, 10)))
        self.matrix.setDescription("Generated at " + datetime.datetime.now().strftime("%I:%M%p on %B %d, %Y"))
        self.new_matrix = self.matrix

    def tearDown(self) -> None:
        try:
            del self.matrix
            os.remove(self.name_test) if os.path.exists(self.name_test) else None
            os.remove(self.csv_export_name) if os.path.exists(self.csv_export_name) else None
            os.remove(self.copy_matrix_name) if os.path.exists(self.copy_matrix_name) else None
            os.remove(self.omx_export_name) if os.path.exists(self.omx_export_name) else None
        except Exception as e:
            print(f"Could not delete.  {e.args}")

    def test_load(self):
        self.new_matrix = AequilibraeMatrix()
        # Cannot load OMX file with no indices
        with self.assertRaises(LookupError):
            self.new_matrix.load(no_index_omx)

        self.new_matrix = AequilibraeMatrix()
        self.new_matrix.load(self.name_test)
        del self.new_matrix

    def test_computational_view(self):
        self.new_matrix.computational_view(["mat", "seed"])
        self.new_matrix.mat.fill(0)
        self.new_matrix.seed.fill(0)
        if self.new_matrix.matrix_view.shape[2] != 2:
            self.fail("Computational view returns the wrong number of matrices")

        self.new_matrix.computational_view(["mat"])
        self.new_matrix.matrix_view[:, :] = np.arange(zones ** 2).reshape(zones, zones)
        if np.sum(self.new_matrix.mat) != np.sum(self.new_matrix.matrix_view):
            self.fail("Assigning to matrix view did not work")
        self.new_matrix.setName("Test matrix - " + str(random.randint(1, 10)))
        self.new_matrix.setDescription("Generated at " + datetime.datetime.now().strftime("%I:%M%p on %B %d, %Y"))
        del self.new_matrix

    def test_computational_view_with_omx(self):
        self.new_matrix = AequilibraeMatrix()
        self.new_matrix.load(omx_example)

        arrays = ["m1", "m2"]
        self.new_matrix.computational_view(arrays)
        total_mats = np.sum(self.new_matrix.matrix_view)

        self.new_matrix.computational_view([arrays[0]])
        total_m1 = np.sum(self.new_matrix.matrix_view)

        self.new_matrix.close()

        omx_file = omx.open_file(omx_example, "r")

        m1 = np.array(omx_file["m1"]).sum()
        m2 = np.array(omx_file["m2"]).sum()

        self.assertEqual(m1 + m2, total_mats)
        self.assertEqual(m1, total_m1)

        omx_file.close()
        del omx_file

    def test_copy(self):
        # test in-memory matrix_procedures copy

        matrix_copy = self.new_matrix.copy(self.copy_matrix_name, cores=["mat"])

        if not np.array_equal(matrix_copy.mat, self.new_matrix.mat):
            self.fail("Matrix copy was not perfect")
        matrix_copy.close()
        del matrix_copy

    def test_export_to_csv(self):
        self.new_matrix.export(self.csv_export_name)
        df = pd.read_csv(self.csv_export_name)
        df.fillna(0, inplace=True)
        self.assertEqual(df.shape[0], 2500, "Exported wrong size")
        self.assertEqual(df.shape[1], 5, "Exported wrong size")
        self.assertAlmostEqual(df.mat.sum(), np.nansum(self.new_matrix.matrices), 5, "Exported wrong matrix total")

    def test_export_to_omx(self):
        self.new_matrix.export(self.omx_export_name)

        omxfile = omx.open_file(self.omx_export_name, "r")

        # Check if matrices values are compatible
        for m in self.new_matrix.names:
            sm = np.nansum(self.new_matrix.matrix[m])
            sm2 = np.nansum(np.array(omxfile[m]))

            self.assertEqual(sm, sm2, "Matrix {} was exported with the wrong value".format(m))
        del omxfile

    def test_nan_to_num(self):
        m = self.new_matrix.mat.sum() - self.new_matrix.mat[1, 1]
        self.new_matrix.computational_view(["mat", "seed"])
        self.new_matrix.nan_to_num()
        self.new_matrix.mat[1, 1] = np.nan
        self.new_matrix.computational_view(["mat"])
        self.new_matrix.nan_to_num()

        if abs(m - self.new_matrix.mat.sum()) > 0.000000000001:
            self.fail("Total for mat matrix not maintained")
        del self.new_matrix

    def test_copy_from_omx(self):
        temp_file = AequilibraeMatrix().random_name()
        a = AequilibraeMatrix()
        a.create_from_omx(temp_file, omx_example)

        omxfile = omx.open_file(omx_example, "r")

        # Check if matrices values are compatible
        for m in ["m1", "m2", "m3"]:
            sm = a.matrix[m].sum()
            sm2 = np.array(omxfile[m]).sum()
            if sm != sm2:
                self.fail("Matrix {} was copied with the wrong value".format(m))

        if np.any(a.index[:] != np.array(list(omxfile.mapping("taz").keys()))):
            self.fail("Index was not created properly")
        a.close()
        del a
        del omxfile

    def test_copy_from_omx_long_name(self):

        temp_file = AequilibraeMatrix().random_name()
        a = AequilibraeMatrix()

        with self.assertRaises(ValueError):
            a.create_from_omx(temp_file, omx_example, robust=False)
        del a

    def test_copy_omx_wrong_content(self):
        # Check if we get a result if we try to copy non-existing cores
        temp_file = AequilibraeMatrix().random_name()
        a = AequilibraeMatrix()

        with self.assertRaises(ValueError):
            a.create_from_omx(temp_file, omx_example, cores=["m1", "m2", "m3", "m4"])

        with self.assertRaises(ValueError):
            a.create_from_omx(temp_file, omx_example, mappings=["wrong index"])
        del a

    def test_get_matrix(self):
        a = AequilibraeMatrix()
        a.load(self.sf_skims)

        with self.assertRaises(AttributeError):
            a.get_matrix("does not exist")

        q = a.get_matrix("distance")
        self.assertEqual(q.shape[0], 24)

        a = AequilibraeMatrix()
        a.load(self.name_test)
        print(np.array_equal(a.get_matrix("seed"), a.matrix["seed"]))

        del a

    def test_save(self):
        a = AequilibraeMatrix()
        a.load(self.sf_skims)

        a.computational_view(["distance"])
        new_mat = np.random.rand(a.zones, a.zones)
        a.matrix_view *= new_mat

        res = a.matrix_view.sum()

        a.save("new_name_for_matrix")
        self.assertEqual(res, a.matrix_view.sum(), "Saved wrong result")

        a.save(["new_name_for_matrix2"])
        self.assertEqual(a.view_names[0], "new_name_for_matrix2", "Did not update computational view")
        self.assertEqual(len(a.view_names), 1, "computational view with the wrong number of matrices")

        a.computational_view(["distance", "new_name_for_matrix"])

        with self.assertRaises(ValueError):
            a.save(["just_one_name"])

        a.save(["one_name", "two_names"])

        with self.assertRaises(ValueError):
            a.save("distance")

        b = AequilibraeMatrix()
        b.load(self.name_test)
        b.computational_view("seed")
        b.save()
        b.computational_view(["mat", "seed", "dist"])
        b.save()
コード例 #14
0
    def save_skims(self, matrix_name: str, which_ones="final", format="omx") -> None:
        """Saves the skims (if any) to the skim folder and registers in the matrix list

        Args:
            name (:obj:`str`): Name of the matrix record to hold this matrix (same name used for file name)
            which_ones (:obj:`str`,optional): {'final': Results of the final iteration, 'blended': Averaged results for
            all iterations, 'all': Saves skims for both the final iteration and the blended ones} Default is 'final'
            *format* (:obj:`str`, `Optional`): File format ('aem' or 'omx'). Default is 'omx'
        """

        mat_format = format.lower()
        if mat_format not in ["omx", "aem"]:
            raise ValueError("Matrix needs to be either OMX or native AequilibraE")
        if mat_format == "omx" and not has_omx:
            raise ImportError("OpenMatrix is not available on your system")

        file_name = f"{matrix_name}.{mat_format}"

        mats = Matrices()
        export_name = path.join(mats.fldr, file_name)

        if path.isfile(export_name):
            raise FileExistsError(f"{file_name} already exists. Choose a different name or matrix format")

        if mats.check_exists(matrix_name):
            raise FileExistsError(f"{matrix_name} already exists. Choose a different name")

        avg_skims = self.classes[0].results.skims  # type: AequilibraeMatrix

        # The ones for the last iteration are here
        last_skims = self.classes[0]._aon_results.skims  # type: AequilibraeMatrix

        names = []
        if which_ones in ["final", "all"]:
            for core in last_skims.names:
                names.append(f"{core}_final")

        if which_ones in ["blended", "all"]:
            for core in avg_skims.names:
                names.append(f"{core}_blended")

        if not names:
            raise ValueError("No skims to save")
        # Assembling a single final skim file can be done like this
        # We will want only the time for the last iteration and the distance averaged out for all iterations
        working_name = export_name if mat_format == "aem" else AequilibraeMatrix().random_name()

        kwargs = {"file_name": working_name, "zones": self.classes[0].graph.centroids.shape[0], "matrix_names": names}

        # Create the matrix to manipulate
        out_skims = AequilibraeMatrix()
        out_skims.create_empty(**kwargs)

        out_skims.index[:] = self.classes[0].graph.centroids[:]
        out_skims.description = f"Assignment skim from procedure ID {self.procedure_id}"

        if which_ones in ["final", "all"]:
            for core in last_skims.names:
                out_skims.matrix[f"{core}_final"][:, :] = last_skims.matrix[core][:, :]

        if which_ones in ["blended", "all"]:
            for core in avg_skims.names:
                out_skims.matrix[f"{core}_blended"][:, :] = avg_skims.matrix[core][:, :]

        out_skims.matrices.flush()  # Make sure that all data went to the disk

        # If it were supposed to be an OMX, we export to one
        if mat_format == "omx":
            out_skims.export(export_name)

        # Now we create the appropriate record
        record = mats.new_record(matrix_name, file_name)
        record.procedure_id = self.procedure_id
        record.timestamp = self.procedure_date
        record.procedure = "Traffic Assignment"
        record.description = "Skimming for assignment procedure"
        record.save()
コード例 #15
0
    def produce_all_outputs(self):
        fn = os.path.join(self.output_path, "skims.aem")

        fn_omx = os.path.join(self.output_path, "skims.omx")
        if self.cb_choose_algorithm.currentText() == 'all-or-nothing':
            cls = [x for x in self.traffic_classes.values()
                   if x is not None][0]
            cls.results.save_to_disk(os.path.join(
                self.output_path, f"link_flows_{cls.graph.mode}.csv"),
                                     output="loads")
            cls.results.save_to_disk(os.path.join(
                self.output_path, f"link_flows_{cls.graph.mode}.aed"),
                                     output="loads")
            if has_omx:
                cls.results.skims.export(fn_omx)
            else:
                cls.results.skims.export(fn)
            return

        table = self.skim_list_table
        skim_names = []
        for i in range(table.rowCount()):
            mode = self.all_modes[table.item(i, 0).text()]
            field = table.item(i, 1).text()
            last_iter = table.cellWidget(i, 2).isChecked()
            blended = table.cellWidget(i, 3).isChecked()
            if last_iter:
                skim_names.append(f'{field}_{mode}_final')
            if blended:
                skim_names.append(f'{field}_{mode}_blended')

        for cls in self.assignment.classes:
            cls.results.save_to_disk(os.path.join(
                self.output_path, f"link_flows_{cls.graph.mode}.csv"),
                                     output="loads")
            cls.results.save_to_disk(os.path.join(
                self.output_path, f"link_flows_{cls.graph.mode}.aed"),
                                     output="loads")

        # cls.results.skims.export(os.path.join(self.output_path, f'blended_skims_{cls.graph.mode}.aed'))
        if skim_names:
            args = {
                'file_name': fn,
                'zones': self.project.network.count_centroids(),
                'matrix_names': skim_names
            }
            skims = AequilibraeMatrix()
            skims.create_empty(**args)

            for i in range(table.rowCount()):
                mode_name = table.item(i, 0).text()
                mode = self.all_modes[mode_name]
                field = table.item(i, 1).text()
                last_iter = table.cellWidget(i, 2).isChecked()
                blended = table.cellWidget(i, 3).isChecked()

                cls = self.traffic_classes[mode_name]

                if last_iter:
                    mat_name = f'{field}_{mode}_final'
                    skims.matrix[
                        mat_name][:, :] = cls._aon_results.skims.matrix[
                            field][:, :]
                if blended:
                    mat_name = f'{field}_{mode}_blended'
                    skims.matrix[mat_name][:, :] = cls.results.skims.matrix[
                        field][:, :]

            skims.index[:] = cls.matrix.index[:]
            if has_omx:
                skims.export(fn_omx)
                skims.close()
                del skims
                os.unlink(fn)
コード例 #16
0
column_vector.create_empty(**args)
column_vector.index[:] = np.arange(column_vector.entries) + 100
column_vector.columns[:] = column_vector.index[:] + np.random.rand(zones)[:]

# balance vectors
column_vector.columns[:] = column_vector.columns[:] * (row_vector.rows.sum() / column_vector.columns.sum())

# Impedance matrix_procedures
name_test = os.path.join(tempfile.gettempdir(), 'aequilibrae_matrix_test.aem')

args = {'file_name': name_test,
        'zones': zones,
        'matrix_names': ['impedance']}

matrix = AequilibraeMatrix()
matrix.create_empty(**args)

# randoms = np.random.randint(5, size=(2, 4))
matrix.impedance[:, :] = np.random.rand(zones, zones)[:, :]
matrix.index[:] = np.arange(matrix.zones) + 100
matrix.computational_view(['impedance'])

model_expo = SyntheticGravityModel()
model_expo.function = 'EXPO'
model_expo.beta = 0.1

model_gamma = SyntheticGravityModel()
model_gamma.function = 'GAMMA'
model_gamma.beta = 0.1
model_gamma.alpha = -0.2
コード例 #17
0
from aequilibrae.matrix import AequilibraeMatrix
from aequilibrae.distribution import GravityCalibration
import numpy as np
import os, tempfile

zones = 100

# Impedance matrix_procedures
name_test = AequilibraeMatrix().random_name()

args = {'file_name': name_test,
        'zones': zones,
        'matrix_names': ['impedance']}

impedance = AequilibraeMatrix()
impedance.create_empty(**args)
impedance.impedance[:, :] = np.random.rand(zones, zones)[:,:]  * 1000
impedance.index[:] = np.arange(impedance.zones) + 100
impedance.computational_view(['impedance'])

args['matrix_names'] = ['base_matrix']
args['file_name'] = AequilibraeMatrix().random_name()
matrix = AequilibraeMatrix()
matrix.create_empty(**args)
matrix.base_matrix[:, :] = np.random.rand(zones, zones)[:,:]  * 1000
matrix.index[:] = np.arange(matrix.zones) + 100
matrix.computational_view(['base_matrix'])



class TestGravityCalibration(TestCase):
コード例 #18
0
class AssignmentResults:
    """
    Assignment result holder for a single :obj:`TrafficClass` with multiple user classes
    """

    def __init__(self):
        self.compact_link_loads = np.array([])  # Results for assignment on simplified graph
        self.compact_total_link_loads = np.array([])  # Results for all user classes summed on simplified graph
        self.link_loads = np.array([])  # The actual results for assignment
        self.total_link_loads = np.array([])  # The result of the assignment for all user classes summed
        self.crosswalk = np.array([])  # crosswalk between compact graph link IDs and actual link IDs
        self.skims = AequilibraeMatrix()  # The array of skims
        self.no_path = None  # The list os paths
        self.num_skims = 0  # number of skims that will be computed. Depends on the setting of the graph provided
        p = Parameters().parameters["system"]["cpus"]
        if not isinstance(p, int):
            p = 0
        self.set_cores(p)

        self.classes = {"number": 1, "names": ["flow"]}

        self.nodes = -1
        self.zones = -1
        self.links = -1
        self.compact_links = -1
        self.compact_nodes = -1
        self.__graph_id__ = None
        self.__float_type = None
        self.__integer_type = None

        self.lids = None
        self.direcs = None

    # In case we want to do by hand, we can prepare each method individually
    def prepare(self, graph: Graph, matrix: AequilibraeMatrix) -> None:
        """
        Prepares the object with dimensions corresponding to the assignment matrix and graph objects

        Args:
            *graph* (:obj:`Graph`): Needs to have been set with number of centroids and list of skims (if any)

            *matrix* (:obj:`AequilibraeMatrix`): Matrix properly set for computation with
             matrix.computational_view(:obj:`list`)
        """

        self.__float_type = graph.default_types("float")
        self.__integer_type = graph.default_types("int")

        if matrix.view_names is None:
            raise ("Please set the matrix_procedures computational view")
        else:
            self.classes["number"] = 1
            if len(matrix.matrix_view.shape) > 2:
                self.classes["number"] = matrix.matrix_view.shape[2]
            self.classes["names"] = matrix.view_names

        if graph is None:
            raise ("Please provide a graph")
        else:

            self.compact_nodes = graph.compact_num_nodes
            self.compact_links = graph.compact_num_links

            self.nodes = graph.num_nodes
            self.zones = graph.num_zones
            self.centroids = graph.centroids
            self.links = graph.num_links
            self.num_skims = len(graph.skim_fields)
            self.skim_names = [x for x in graph.skim_fields]
            self.lids = graph.graph.link_id.values
            self.direcs = graph.graph.direction.values
            self.crosswalk = np.zeros(graph.graph.shape[0], self.__integer_type)
            self.crosswalk[graph.graph.__supernet_id__.values] = graph.graph.__compressed_id__.values
            self.__graph_ids = graph.graph.__supernet_id__.values
            self.__redim()
            self.__graph_id__ = graph.__id__

    def reset(self) -> None:
        """
        Resets object to prepared and pre-computation state
        """
        if self.num_skims > 0:
            self.skims.matrices.fill(0)
        if self.link_loads is not None:
            self.no_path.fill(0)
            self.link_loads.fill(0)
            self.total_link_loads.fill(0)
            self.compact_link_loads.fill(0)
            self.compact_total_link_loads.fill(0)
        else:
            raise ValueError("Exception: Assignment results object was not yet prepared/initialized")

    def __redim(self):
        self.compact_link_loads = np.zeros((self.compact_links + 1, self.classes["number"]), self.__float_type)
        self.compact_total_link_loads = np.zeros(self.compact_links, self.__float_type)

        self.link_loads = np.zeros((self.links, self.classes["number"]), self.__float_type)
        self.total_link_loads = np.zeros(self.links, self.__float_type)
        self.no_path = np.zeros((self.zones, self.zones), dtype=self.__integer_type)

        if self.num_skims > 0:
            self.skims = AequilibraeMatrix()

            self.skims.create_empty(file_name=self.skims.random_name(), zones=self.zones, matrix_names=self.skim_names)
            self.skims.index[:] = self.centroids[:]
            self.skims.computational_view()
            if len(self.skims.matrix_view.shape[:]) == 2:
                self.skims.matrix_view = self.skims.matrix_view.reshape((self.zones, self.zones, 1))
        else:
            self.skims = AequilibraeMatrix()
            self.skims.matrix_view = np.array((1, 1, 1))

        self.reset()

    def total_flows(self) -> None:
        """ Totals all link flows for this class into a single link load

        Results are placed into *total_link_loads* class member
        """
        sum_axis1(self.total_link_loads, self.link_loads, self.cores)

    def set_cores(self, cores: int) -> None:
        """
        Sets number of cores (threads) to be used in computation

        Value of zero sets number of threads to all available in the system, while negative values indicate the number
        of threads to be left out of the computational effort.

        Resulting number of cores will be adjusted to a minimum of zero or the maximum available in the system if the
        inputs result in values outside those limits

        Args:
            *cores* (:obj:`int`): Number of cores to be used in computation
        """

        if not isinstance(cores, int):
            raise ValueError("Number of cores needs to be an integer")

        if cores < 0:
            self.cores = max(1, mp.cpu_count() + cores)
        elif cores == 0:
            self.cores = mp.cpu_count()
        elif cores > 0:
            cores = min(mp.cpu_count(), cores)
            if self.cores != cores:
                self.cores = cores
        if self.link_loads.shape[0]:
            self.__redim()

    def get_load_results(self) -> AequilibraeData:
        """
        Translates the assignment results from the graph format into the network format

        Returns:
            dataset (:obj:`AequilibraeData`): AequilibraE data with the traffic class assignment results
        """
        fields = []
        for n in self.classes["names"]:
            fields.extend([f"{n}_ab", f"{n}_ba", f"{n}_tot"])
        types = [np.float64] * len(fields)

        entries = int(np.unique(self.lids).shape[0])
        res = AequilibraeData()
        res.create_empty(memory_mode=True, entries=entries, field_names=fields, data_types=types)
        res.data.fill(np.nan)
        res.index[:] = np.unique(self.lids)[:]

        indexing = np.zeros(int(self.lids.max()) + 1, np.uint64)
        indexing[res.index[:]] = np.arange(entries)

        # Indices of links BA and AB
        ABs = self.direcs > 0
        BAs = self.direcs < 0
        ab_ids = indexing[self.lids[ABs]]
        ba_ids = indexing[self.lids[BAs]]

        # Link flows
        link_flows = self.link_loads[self.__graph_ids, :]
        for i, n in enumerate(self.classes["names"]):
            # AB Flows
            res.data[n + "_ab"][ab_ids] = np.nan_to_num(link_flows[ABs, i])
            # BA Flows
            res.data[n + "_ba"][ba_ids] = np.nan_to_num(link_flows[BAs, i])

            # Tot Flow
            res.data[n + "_tot"] = np.nan_to_num(res.data[n + "_ab"]) + np.nan_to_num(res.data[n + "_ba"])
        return res

    def save_to_disk(self, file_name=None, output="loads") -> None:
        """ Function to write to disk all outputs computed during assignment

        Args:
            *file_name* (:obj:`str`): Name of the file, with extension. Valid extensions are: ['aed', 'csv', 'sqlite']
            *output* (:obj:`str`, optional): Type of output ('loads', 'path_file'). Defaults to 'loads'
        """

        if output == "loads":
            res = self.get_load_results()
            res.export(file_name)

        # TODO: Re-factor the exporting of the path file within the AequilibraeData format
        elif output == "path_file":
            raise NotImplementedError
コード例 #19
0
from aequilibrae.matrix import AequilibraeMatrix
from aequilibrae.distribution import GravityCalibration
import numpy as np
import os, tempfile

zones = 100

# Impedance matrix_procedures
name_test = AequilibraeMatrix().random_name()

args = {'file_name': name_test,
        'zones': zones,
        'matrix_names': ['impedance']}

impedance = AequilibraeMatrix()
impedance.create_empty(**args)
impedance.impedance[:, :] = np.random.rand(zones, zones)[:,:]  * 1000
impedance.index[:] = np.arange(impedance.zones) + 100
impedance.computational_view(['impedance'])

args['matrix_names'] = ['base_matrix']
matrix = AequilibraeMatrix()
matrix.create_empty(**args)
matrix.base_matrix[:, :] = np.random.rand(zones, zones)[:,:]  * 1000
matrix.index[:] = np.arange(matrix.zones) + 100
matrix.computational_view(['base_matrix'])



class TestGravityCalibration(TestCase):
    def test_calibrate(self):