Exemplo n.º 1
0
    def test_skimming_on_assignment(self):
        matrix = AequilibraeMatrix()
        matrix.load(os.path.join(gettempdir(), self.mat_name))
        matrix.computational_view(["cars"])

        res = AssignmentResults()

        res.prepare(self.g, matrix)

        self.g.set_skimming([])
        self.g.set_blocked_centroid_flows(True)
        assig = allOrNothing(matrix, self.g, res)
        assig.execute()

        if res.skims.distance.sum() > 0:
            self.fail(
                "skimming for nothing during assignment returned something different than zero"
            )

        self.g.set_skimming("distance")
        res.prepare(self.g, matrix)

        assig = allOrNothing(matrix, self.g, res)
        assig.execute()
        if res.skims.distance.sum() != 2914644.0:
            self.fail("skimming during assignment returned the wrong value")
        matrix.close()
Exemplo n.º 2
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}
Exemplo n.º 3
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)
Exemplo n.º 4
0
    def test_execute(self):
        # Loads and prepares the graph

        car_loads = []
        two_class_loads = []

        for extension in ["omx", "aem"]:
            matrix = AequilibraeMatrix()
            if extension == 'omx':
                mat_name = os.path.join(gettempdir(), "my_matrix." + extension)
            else:
                mat_name = self.mat_name
            matrix.load(mat_name)

            matrix.computational_view(["cars"])

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

            assig = allOrNothing(matrix, self.g, res)
            assig.execute()
            car_loads.append(res.link_loads)
            res.save_to_disk(
                os.path.join(gettempdir(),
                             "link_loads_{}.aed".format(extension)))
            res.save_to_disk(
                os.path.join(gettempdir(),
                             "link_loads_{}.csv".format(extension)))

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

            assig = allOrNothing(matrix, self.g, res)
            assig.execute()
            two_class_loads.append(res.link_loads)
            res.save_to_disk(
                os.path.join(gettempdir(),
                             "link_loads_2_classes_{}.aed".format(extension)))
            res.save_to_disk(
                os.path.join(gettempdir(),
                             "link_loads_2_classes_{}.csv".format(extension)))
            matrix.close()

        load_diff = two_class_loads[0] - two_class_loads[1]
        if load_diff.max() > 0.0000000001 or load_diff.max() < -0.0000000001:
            self.fail(
                "Loads for two classes differ for OMX and AEM matrix types")

        load_diff = car_loads[0] - car_loads[1]
        if load_diff.max() > 0.0000000001 or load_diff.max() < -0.0000000001:
            self.fail(
                "Loads for a single class differ for OMX and AEM matrix types")
Exemplo n.º 5
0
    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()
Exemplo n.º 6
0
    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()
Exemplo n.º 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': '/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')
Exemplo n.º 8
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"))
Exemplo n.º 9
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'))
Exemplo n.º 10
0
    def test_calibrate_with_omx(self):
        imped = AequilibraeMatrix()
        imped.load(siouxfalls_skims)
        imped.computational_view(['free_flow_time'])

        mat = AequilibraeMatrix()
        mat.load(siouxfalls_demand)
        mat.computational_view()

        args = {"impedance": imped, "matrix": mat, "function": "power", "nan_to_zero": False}

        distributed_matrix = GravityCalibration(**args)
        distributed_matrix.calibrate()
        if distributed_matrix.gap > 0.0001:
            self.fail("Calibration did not converge")

        args = {"impedance": imped, "matrix": mat, "function": "power", "nan_to_zero": True}

        distributed_matrix = GravityCalibration(**args)
        distributed_matrix.calibrate()
        if distributed_matrix.gap > 0.0001:
            self.fail("Calibration did not converge")
Exemplo n.º 11
0
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):
        args = {'impedance': impedance,
                'matrix': matrix,
                'function': 'power',
Exemplo n.º 12
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()
Exemplo n.º 13
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
Exemplo n.º 14
0
column_vector = AequilibraEData()
column_vector.create_empty(**args)
column_vector.columns[:] = np.random.rand(zones)[:] * 1000
column_vector.index[:] = np.arange(zones)[:]
# balance vectors
column_vector.columns[:] = column_vector.columns[:] * (
    row_vector.rows.sum() / column_vector.columns.sum())

# seed matrix_procedures
name_test = AequilibraeMatrix().random_name()
args = {"file_name": name_test, "zones": zones, "matrix_names": ["seed"]}

matrix = AequilibraeMatrix()
matrix.create_empty(**args)
matrix.seed[:, :] = np.random.rand(zones, zones)[:, :]
matrix.computational_view(["seed"])
matrix.matrix_view[1, 1] = np.nan
matrix.index[:] = np.arange(zones)[:]


class TestIpf(TestCase):
    def test_fit(self):
        # The IPF per se
        args = {
            "matrix": matrix,
            "rows": row_vector,
            "row_field": "rows",
            "columns": column_vector,
            "column_field": "columns",
            "nan_as_zero": False,
        }
Exemplo n.º 15
0
# 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

model_power = SyntheticGravityModel()
model_power.function = 'POWER'
model_power.alpha = -0.2

Exemplo n.º 16
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_()
Exemplo n.º 17
0
# 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

model_power = SyntheticGravityModel()
model_power.function = "POWER"
model_power.alpha = -0.2

class TestAequilibraeMatrix(TestCase):
    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

    def test_load(self):
        # self.test___init__()
        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(name_test)

    def test_computational_view(self):
        self.test_load()
        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"))
        self.new_matrix.close()

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

    def test_copy(self):
        self.test_load()

        # test in-memory matrix_procedures copy

        matrix_copy = self.new_matrix.copy(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()
        self.new_matrix.close()

    def test_export_to_csv(self):
        self.test_load()
        self.new_matrix.export(csv_export_name)
        self.new_matrix.close()

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

        omxfile = omx.open_file(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))

        self.new_matrix.close()

    def test_nan_to_num(self):
        self.test_load()
        s = self.new_matrix.seed.sum() - self.new_matrix.seed[1, 1]
        m = self.new_matrix.mat.sum() - self.new_matrix.mat[1, 1]
        self.new_matrix.seed[1, 1] = np.nan
        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 s != self.new_matrix.seed.sum():
            self.fail("Total for seed matrix not maintained")

        if m != self.new_matrix.mat.sum():
            self.fail("Total for mat matrix not maintained")

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

    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)

    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"])
class DisplayAequilibraEFormatsDialog(QtWidgets.QDialog, FORM_CLASS):
    def __init__(self, iface):
        QtWidgets.QDialog.__init__(self)
        self.iface = iface
        self.setupUi(self)

        self.error = None

        self.error = None
        self.data_path, self.data_type = GetOutputFileName(
            self, 'AequilibraE custom formats',
            ["Aequilibrae dataset(*.aed)", "Aequilibrae matrix(*.aem)"],
            '.aed', standard_path())

        if self.data_type is None:
            self.error = 'Path provided is not a valid dataset'
            self.exit_with_error()

        self.data_type = self.data_type.upper()

        if self.data_type == 'AED':
            self.data_to_show = AequilibraEData()
        elif self.data_type == 'AEM':
            self.data_to_show = AequilibraeMatrix()

        try:
            self.data_to_show.load(self.data_path)
        except:
            self.error = 'Could not load dataset'
            self.exit_with_error()

        # Elements that will be used during the displaying
        self._layout = QVBoxLayout()
        self.table = QTableView()
        self._layout.addWidget(self.table)

        # Settings for displaying
        self.show_layout = QHBoxLayout()

        # Thousand separator
        self.thousand_separator = QCheckBox()
        self.thousand_separator.setChecked(True)
        self.thousand_separator.setText('Thousands separator')
        self.thousand_separator.toggled.connect(self.format_showing)
        self.show_layout.addWidget(self.thousand_separator)

        self.spacer = QSpacerItem(5, 0, QtWidgets.QSizePolicy.Expanding,
                                  QtWidgets.QSizePolicy.Minimum)
        self.show_layout.addItem(self.spacer)

        # Decimals
        txt = QLabel()
        txt.setText('Decimal places')
        self.show_layout.addWidget(txt)
        self.decimals = QSpinBox()
        self.decimals.valueChanged.connect(self.format_showing)
        self.decimals.setMinimum(0)
        self.decimals.setValue(4)
        self.decimals.setMaximum(10)

        self.show_layout.addWidget(self.decimals)
        self._layout.addItem(self.show_layout)

        # differentiates between matrix and dataset
        if self.data_type == 'AEM':
            self.data_to_show.computational_view([self.data_to_show.names[0]])
            # Matrices need cores and indices to be set as well
            self.mat_layout = QHBoxLayout()
            self.mat_list = QComboBox()
            for n in self.data_to_show.names:
                self.mat_list.addItem(n)

            self.mat_list.currentIndexChanged.connect(self.change_matrix_cores)
            self.mat_layout.addWidget(self.mat_list)

            self.idx_list = QComboBox()
            for i in self.data_to_show.index_names:
                self.idx_list.addItem(i)
            self.idx_list.currentIndexChanged.connect(self.change_matrix_cores)
            self.mat_layout.addWidget(self.idx_list)
            self._layout.addItem(self.mat_layout)
            self.change_matrix_cores()

        self.but_export = QPushButton()
        self.but_export.setText('Export')
        self.but_export.clicked.connect(self.export)

        self.but_close = QPushButton()
        self.but_close.clicked.connect(self.exit_procedure)
        self.but_close.setText('Close')

        self.but_layout = QHBoxLayout()
        self.but_layout.addWidget(self.but_export)
        self.but_layout.addWidget(self.but_close)

        self._layout.addItem(self.but_layout)

        # We chose to use QTableView. However, if we want to allow the user to edit the dataset
        # The we need to allow them to switch to the slower QTableWidget
        # Code below

        # self.table = QTableWidget(self.data_to_show.entries, self.data_to_show.num_fields)
        # self.table.setHorizontalHeaderLabels(self.data_to_show.fields)
        # self.table.setObjectName('data_viewer')
        #
        # self.table.setVerticalHeaderLabels([str(x) for x in self.data_to_show.index[:]])
        # self.table.clearContents()
        #
        # for i in range(self.data_to_show.entries):
        #     for j, f in enumerate(self.data_to_show.fields):
        #         item1 = QTableWidgetItem(str(self.data_to_show.data[f][i]))
        #         item1.setFlags(Qt.ItemIsEnabled | Qt.ItemIsSelectable)
        #         self.table.setItem(i, j, item1)

        self.resize(700, 500)
        self.setLayout(self._layout)
        self.format_showing()

    def format_showing(self):
        decimals = self.decimals.value()
        separator = self.thousand_separator.isChecked()
        if isinstance(self.data_to_show, AequilibraeMatrix):
            m = NumpyModel(self.data_to_show, separator, decimals)
        else:
            m = DatabaseModel(self.data_to_show, separator, decimals)
        self.table.clearSpans()
        self.table.setModel(m)

    def change_matrix_cores(self):
        self.data_to_show.computational_view([self.mat_list.currentText()])
        self.data_to_show.set_index(self.data_to_show.index_names[0])
        self.format_showing()

    def export(self):
        new_name, file_type = GetOutputFileName(
            self, self.data_type, ["Comma-separated file(*.csv)"], ".csv",
            self.data_path)
        if new_name is not None:
            self.data_to_show.export(new_name)

    def exit_with_error(self):
        qgis.utils.iface.messageBar().pushMessage("Error:",
                                                  self.error,
                                                  level=1)
        self.exit_procedure()

    def exit_procedure(self):
        self.close()
Exemplo n.º 20
0
class TestTrafficAssignment(TestCase):
    def setUp(self) -> None:
        self.matrix = AequilibraeMatrix()
        self.matrix.load(siouxfalls_demand)
        self.matrix.computational_view()

        self.project = Project()
        self.project.load(siouxfalls_project)
        self.project.network.build_graphs()
        self.car_graph = self.project.network.graphs['c']  # type: Graph
        self.car_graph.set_graph('free_flow_time')
        self.car_graph.set_blocked_centroid_flows(False)

        self.assignment = TrafficAssignment()
        self.assigclass = TrafficClass(self.car_graph, self.matrix)

    def tearDown(self) -> None:
        self.matrix.close()
        self.project.conn.close()

    def test_set_vdf(self):
        with self.assertRaises(ValueError):
            self.assignment.set_vdf('CQS')

        self.assignment.set_vdf('BPR')

    def test_set_classes(self):
        with self.assertRaises(ValueError):
            self.assignment.set_classes([1, 2])

        # The traffic assignment class is unprotected.
        # Should we protect it?
        # self.assigclass = TrafficClass(self.car_graph, self.matrix)
        # self.assigclass.graph = 1
        # with self.assertRaises(ValueError):
        #     self.assignment.set_classes(self.assigclass)

        self.assignment.set_classes(self.assigclass)
        # self.fail()

    def test_algorithms_available(self):
        algs = self.assignment.algorithms_available()
        real = ['all-or-nothing', 'msa', 'frank-wolfe', 'bfw', 'cfw']

        diff = [x for x in real if x not in algs]
        diff2 = [x for x in algs if x not in real]

        if len(diff) + len(diff2) > 0:
            self.fail('list of algorithms raised is wrong')

    def test_set_cores(self):
        with self.assertRaises(Exception):
            self.assignment.set_cores(3)

        self.assignment.set_classes(self.assigclass)
        with self.assertRaises(ValueError):
            self.assignment.set_cores('q')

        self.assignment.set_cores(3)

    def test_set_algorithm(self):
        with self.assertRaises(AttributeError):
            self.assignment.set_algorithm('not an algo')

        self.assignment.set_classes(self.assigclass)

        with self.assertRaises(Exception):
            self.assignment.set_algorithm('msa')

        self.assignment.set_vdf("BPR")
        self.assignment.set_vdf_parameters({"alpha": "b", "beta": "power"})

        self.assignment.set_capacity_field("capacity")
        self.assignment.set_time_field("free_flow_time")

        self.assignment.max_iter = 10
        self.assignment.set_algorithm('bfw')

    def test_set_vdf_parameters(self):
        with self.assertRaises(Exception):
            self.assignment.set_vdf_parameters({"alpha": "b", "beta": "power"})

        self.assignment.set_vdf('bpr')
        self.assignment.set_classes(self.assigclass)
        self.assignment.set_vdf_parameters({"alpha": "b", "beta": "power"})

    def test_set_time_field(self):
        N = random.randint(1, 50)
        val = ''.join(
            random.choices(string.ascii_uppercase + string.digits, k=N))
        self.assignment.set_time_field(val)
        self.assertEqual(self.assignment.time_field, val)

    def test_set_capacity_field(self):
        N = random.randint(1, 50)
        val = ''.join(
            random.choices(string.ascii_uppercase + string.digits, k=N))
        self.assignment.set_capacity_field(val)
        self.assertEqual(self.assignment.capacity_field, val)

    def test_execute(self):

        self.assignment.set_classes(self.assigclass)
        self.assignment.set_vdf("BPR")
        self.assignment.set_vdf_parameters({"alpha": 0.15, "beta": 4.0})
        self.assignment.set_vdf_parameters({"alpha": "b", "beta": "power"})

        self.assignment.set_capacity_field("capacity")
        self.assignment.set_time_field("free_flow_time")

        self.assignment.max_iter = 10
        self.assignment.set_algorithm('msa')
        self.assignment.execute()
        msa10 = self.assignment.assignment.rgap

        self.assigclass.results.total_flows()
        correl = np.corrcoef(self.assigclass.results.total_link_loads,
                             self.assigclass.graph.graph['volume'])[0, 1]
        self.assertLess(0.8, correl)

        self.assignment.max_iter = 30
        self.assignment.set_algorithm('msa')
        self.assignment.execute()
        msa25 = self.assignment.assignment.rgap

        self.assigclass.results.total_flows()
        correl = np.corrcoef(self.assigclass.results.total_link_loads,
                             self.assigclass.graph.graph['volume'])[0, 1]
        self.assertLess(0.95, correl)

        self.assignment.set_algorithm('frank-wolfe')
        self.assignment.execute()
        fw25 = self.assignment.assignment.rgap

        self.assigclass.results.total_flows()
        correl = np.corrcoef(self.assigclass.results.total_link_loads,
                             self.assigclass.graph.graph['volume'])[0, 1]
        self.assertLess(0.97, correl)

        self.assignment.set_algorithm('cfw')
        self.assignment.execute()
        cfw25 = self.assignment.assignment.rgap

        self.assigclass.results.total_flows()
        correl = np.corrcoef(self.assigclass.results.total_link_loads,
                             self.assigclass.graph.graph['volume'])[0, 1]
        self.assertLess(0.98, correl)

        self.assignment.set_algorithm('bfw')
        self.assignment.execute()
        bfw25 = self.assignment.assignment.rgap

        self.assigclass.results.total_flows()
        correl = np.corrcoef(self.assigclass.results.total_link_loads,
                             self.assigclass.graph.graph['volume'])[0, 1]
        self.assertLess(0.99, correl)

        self.assertLess(msa25, msa10)
        self.assertLess(fw25, msa25)
        self.assertLess(cfw25, fw25)
        self.assertLess(bfw25, cfw25)
Exemplo n.º 21
0
column_vector = AequilibraEData()
column_vector.create_empty(**args)
column_vector.columns[:] = np.random.rand(zones)[:] * 1000
column_vector.index[:] = np.arange(zones)[:]
# balance vectors
column_vector.columns[:] = column_vector.columns[:] * (
    row_vector.rows.sum() / column_vector.columns.sum())

# seed matrix_procedures
name_test = AequilibraeMatrix().random_name()
args = {'file_name': name_test, 'zones': zones, 'matrix_names': ['seed']}

matrix = AequilibraeMatrix()
matrix.create_empty(**args)
matrix.seed[:, :] = np.random.rand(zones, zones)[:, :]
matrix.computational_view(['seed'])
matrix.matrix_view[1, 1] = np.nan
matrix.index[:] = np.arange(zones)[:]


class TestIpf(TestCase):
    def test_fit(self):
        # The IPF per se
        args = {
            'matrix': matrix,
            'rows': row_vector,
            'row_field': 'rows',
            'columns': column_vector,
            'column_field': 'columns',
            'nan_as_zero': False
        }
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):
    def test_calibrate(self):
        args = {'impedance': impedance,
                'matrix': matrix,
Exemplo n.º 23
0
from aequilibrae.distribution import GravityCalibration
from aequilibrae.matrix import AequilibraeMatrix
from ...data import siouxfalls_demand, siouxfalls_skims

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):
    def test_calibrate(self):
        args = {"impedance": impedance, "matrix": matrix, "function": "power", "nan_to_zero": False}

        distributed_matrix = GravityCalibration(**args)
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

model_power = SyntheticGravityModel()
model_power.function = 'POWER'
model_power.alpha = -0.2

Exemplo n.º 25
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
Exemplo n.º 26
0
class Ipf:
    """Iterative proportional fitting procedure

        ::

            import pandas as pd
            from aequilibrae.distribution import Ipf
            from aequilibrae.matrix import AequilibraeMatrix
            from aequilibrae.matrix import AequilibraeData

            matrix = AequilibraeMatrix()

            # Here we can create from OMX or load from an AequilibraE matrix.
            matrix.create_from_omx(path/to/aequilibrae_matrix, path/to/omxfile)

            # The matrix will be operated one (see the note on overwriting), so it does
            # not make sense load an OMX matrix


            source_vectors = pd.read_csv(path/to/CSVs)
            zones = source_vectors.zone.shape[0]

            args = {"entries": zones, "field_names": ["productions", "attractions"],
                    "data_types": [np.float64, np.float64], "memory_mode": True}

            vectors = AequilibraEData()
            vectors.create_empty(**args)

            vectors.productions[:] = source_vectors.productions[:]
            vectors.attractions[:] = source_vectors.attractions[:]

            # We assume that the indices would be sorted and that they would match the matrix indices
            vectors.index[:] = source_vectors.zones[:]

            args = {
                    "matrix": matrix, "rows": vectors, "row_field": "productions", "columns": vectors,
                    "column_field": "attractions", "nan_as_zero": False}

            fratar = Ipf(**args)

             fratar.fit()

            # We can get back to our OMX matrix in the end
            fratar.output.export(path/to_omx/output.omx)
            fratar.output.export(path/to_aem/output.aem)

"""
    def __init__(self, **kwargs):
        """
        Instantiates the Ipf problem

        Args:
            matrix (:obj:`AequilibraeMatrix`): Seed Matrix

            rows (:obj:`AequilibraeData`): Vector object with data for row totals

            row_field (:obj:`str`): Field name that contains the data for the row totals

            columns (:obj:`AequilibraeData`): Vector object with data for column totals

            column_field (:obj:`str`): Field name that contains the data for the column totals

            parameters (:obj:`str`, optional): Convergence parameters. Defaults to those in the parameter file

            nan_as_zero (:obj:`bool`, optional): If Nan values should be treated as zero. Defaults to True

        Results:
            output (:obj:`AequilibraeMatrix`): Result Matrix

            report (:obj:`list`): Iteration and convergence report

            error (:obj:`str`): Error description
        """
        self.parameters = kwargs.get("parameters",
                                     self.__get_parameters("ipf"))

        # Seed matrix
        self.matrix = kwargs.get("matrix", None)

        # NaN as zero
        self.nan_as_zero = kwargs.get("nan_as_zero", True)

        # row vector
        self.rows = kwargs.get("rows", None)
        self.row_field = kwargs.get("row_field", None)
        self.output_name = kwargs.get("output",
                                      AequilibraeMatrix().random_name())

        # Column vector
        self.columns = kwargs.get("columns", None)
        self.column_field = kwargs.get("column_field", None)

        self.output = AequilibraeMatrix()
        self.error = None
        self.__required_parameters = [
            "convergence level", "max iterations", "balancing tolerance"
        ]
        self.error_free = True
        self.report = ["  #####    IPF computation    #####  ", ""]
        self.gap = None
        self.procedure_date = ''
        self.procedure_id = ''

    def __check_data(self):
        self.error = None
        self.__check_parameters()

        # check data types
        if not isinstance(self.rows, AequilibraeData):
            raise TypeError(
                "Row vector needs to be an instance of AequilibraeData")

        if not isinstance(self.columns, AequilibraeData):
            raise TypeError(
                "Column vector needs to be an instance of AequilibraeData")

        if not isinstance(self.matrix, AequilibraeMatrix):
            raise TypeError(
                "Seed matrix needs to be an instance of AequilibraeMatrix")

        # Check data type
        if not np.issubdtype(self.matrix.dtype, np.floating):
            raise ValueError("Seed matrix need to be a float type")

        row_data = self.rows.data
        col_data = self.columns.data

        if not np.issubdtype(row_data[self.row_field].dtype, np.floating):
            raise ValueError("production/rows vector must be a float type")

        if not np.issubdtype(col_data[self.column_field].dtype, np.floating):
            raise ValueError("Attraction/columns vector must be a float type")

        # Check data dimensions
        if not np.array_equal(self.rows.index, self.columns.index):
            raise ValueError(
                "Indices from row vector do not match those from column vector"
            )

        if not np.array_equal(self.matrix.index, self.columns.index):
            raise ValueError(
                "Indices from vectors do not match those from seed matrix")

        # Check if matrix was set for computation
        if self.matrix.matrix_view is None:
            raise ValueError("Matrix needs to be set for computation")
        else:
            if len(self.matrix.matrix_view.shape[:]) > 2:
                raise ValueError(
                    "Matrix' computational view needs to be set for a single matrix core"
                )

        if self.error is None:
            # check balancing:
            sum_rows = np.nansum(row_data[self.row_field])
            sum_cols = np.nansum(col_data[self.column_field])
            if abs(sum_rows -
                   sum_cols) > self.parameters["balancing tolerance"]:
                self.error = "Vectors are not balanced"
            else:
                # guarantees that they are precisely balanced
                col_data[self.column_field][:] = col_data[
                    self.column_field][:] * (sum_rows / sum_cols)

        if self.error is not None:
            self.error_free = False

    def __check_parameters(self):
        for i in self.__required_parameters:
            if i not in self.parameters:
                self.error = "Parameters error. It needs to be a dictionary with the following keys: "
                for t in self.__required_parameters:
                    self.error = self.error + t + ", "
        if self.error:
            raise ValueError(self.error)

    def fit(self):
        """Runs the IPF instance problem to adjust the matrix

        Resulting matrix is the *output* class member
        """
        self.procedure_id = uuid4().hex
        self.procedure_date = str(datetime.today())
        t = perf_counter()
        self.__check_data()
        if self.error_free:
            max_iter = self.parameters["max iterations"]
            conv_criteria = self.parameters["convergence level"]

            if self.matrix.is_omx():
                self.output = AequilibraeMatrix()
                self.output.create_from_omx(self.output.random_name(),
                                            self.matrix.file_path,
                                            cores=self.matrix.view_names)
                self.output.computational_view()
            else:
                self.output = self.matrix.copy(self.output_name)
            if self.nan_as_zero:
                self.output.matrix_view[:, :] = np.nan_to_num(
                    self.output.matrix_view)[:, :]

            rows = self.rows.data[self.row_field]
            columns = self.columns.data[self.column_field]
            tot_matrix = np.nansum(self.output.matrix_view[:, :])

            # Reporting
            self.report.append("Target convergence criteria: " +
                               str(conv_criteria))
            self.report.append("Maximum iterations: " + str(max_iter))
            self.report.append("")
            self.report.append("Rows:" + str(self.rows.entries))
            self.report.append("Columns: " + str(self.columns.entries))

            self.report.append("Total of seed matrix: " +
                               "{:28,.4f}".format(float(tot_matrix)))
            self.report.append("Total of target vectors: " +
                               "{:25,.4f}".format(float(np.nansum(rows))))
            self.report.append("")
            self.report.append("Iteration,   Convergence")
            self.gap = conv_criteria + 1

            iter = 0
            while self.gap > conv_criteria and iter < max_iter:
                iter += 1
                # computes factors for zones
                marg_rows = self.__tot_rows(self.output.matrix_view[:, :])
                row_factor = self.__factor(marg_rows, rows)
                # applies factor
                self.output.matrix_view[:, :] = np.transpose(
                    np.transpose(self.output.matrix_view[:, :]) *
                    np.transpose(row_factor))[:, :]

                # computes factors for columns
                marg_cols = self.__tot_columns(self.output.matrix_view[:, :])
                column_factor = self.__factor(marg_cols, columns)

                # applies factor
                self.output.matrix_view[:, :] = self.output.matrix_view[:, :] * column_factor

                # increments iterarions and computes errors
                self.gap = max(
                    abs(1 - np.min(row_factor)),
                    abs(np.max(row_factor) - 1),
                    abs(1 - np.min(column_factor)),
                    abs(np.max(column_factor) - 1),
                )

                self.report.append(
                    str(iter) + "   ,   " +
                    str("{:4,.10f}".format(float(np.nansum(self.gap)))))

            self.report.append("")
            self.report.append("Running time: " +
                               str("{:4,.3f}".format(perf_counter() - t)) +
                               "s")

    def save_to_project(self, name: str, file_name: str) -> MatrixRecord:
        """Saves the matrix output to the project file

        Args:
            name (:obj:`str`): Name of the desired matrix record
            file_name (:obj:`str`): Name for the matrix file name. AEM and OMX supported
        """

        mats = Matrices()
        record = mats.new_record(name, file_name, self.output)
        record.procedure_id = self.procedure_id
        record.timestamp = self.procedure_date
        record.procedure = 'Iterative Proportional fitting'
        record.save()
        return record

    def __tot_rows(self, matrix):
        return np.nansum(matrix, axis=1)

    def __tot_columns(self, matrix):
        return np.nansum(matrix, axis=0)

    def __factor(self, marginals, targets):
        f = np.divide(targets, marginals)  # We compute the factors
        f[f == np.NINF] = 1  # And treat the errors
        return f

    def __get_parameters(self, model):
        path = os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))
        with open(path + "/parameters.yml", "r") as yml:
            path = yaml.safe_load(yml)
        return path["distribution"][model]
Exemplo n.º 27
0
class TestAequilibraeMatrix(TestCase):
    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)

    def test_load(self):
        # self.test___init__()
        self.new_matrix = AequilibraeMatrix()
        self.new_matrix.load(name_test)

    def test_computational_view(self):
        self.test_load()
        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"))
        self.new_matrix.close(True)

    def test_copy(self):
        self.test_load()

        # test in-memory matrix_procedures copy

        matrix_copy = self.new_matrix.copy(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(True)
        self.new_matrix.close(True)

    def test_export(self):
        self.test_load()
        self.new_matrix.export(csv_export_name)
        self.new_matrix.close(True)

    def test_nan_to_num(self):
        self.test_load()
        s = self.new_matrix.seed.sum() - self.new_matrix.seed[1, 1]
        m = self.new_matrix.mat.sum() - self.new_matrix.mat[1, 1]
        self.new_matrix.seed[1,1] = np.nan
        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 s != self.new_matrix.seed.sum():
            self.fail('Total for seed matrix not maintained')

        if m != self.new_matrix.mat.sum():
            self.fail('Total for mat matrix not maintained')
Exemplo n.º 28
0
column_vector.create_empty(**args)
column_vector.columns[:] = np.random.rand(zones)[:] * 1000
column_vector.index[:] = np.arange(zones)[:]
# balance vectors
column_vector.columns[:] = column_vector.columns[:] * (row_vector.rows.sum()/column_vector.columns.sum())

# seed matrix_procedures
name_test = AequilibraeMatrix().random_name()
args = {'file_name': name_test,
        'zones': zones,
        'matrix_names': ['seed']}

matrix = AequilibraeMatrix()
matrix.create_empty(**args)
matrix.seed[:, :] = np.random.rand(zones, zones)[:,:]
matrix.computational_view(['seed'])
matrix.matrix_view[1,1] = np.nan
matrix.index[:] = np.arange(zones)[:]


class TestIpf(TestCase):
    def test_fit(self):
        # The IPF per se
        args = {'matrix': matrix,
                'rows': row_vector,
                'row_field': 'rows',
                'columns': column_vector,
                'column_field': 'columns',
                'nan_as_zero': False}

        fratar = Ipf(**args)