Cari tahu apakah dua matriks simetris adalah sama hingga permutasi baris/kolomnya

Saya memiliki dua matriks simetris (kejadian bersama item) A dan B dan ingin mengetahui apakah keduanya menggambarkan kejadian bersama yang sama, hanya dengan label baris/kolom yang diubah. (Permutasi yang sama harus diterapkan pada baris dan kolom untuk menjaga properti simetri/kejadian bersama)

Misalnya kedua matriks ini harus sama dalam pengujian saya:

a = np.array([
    #1 #2 #3 #4 #5 #6 #7
    [0, 1, 1, 0, 0, 0, 1], #1
    [1, 0, 1, 2, 1, 1, 2], #2
    [1, 1, 0, 0, 0, 0, 1], #3
    [0, 2, 0, 0, 4, 0, 4], #4
    [0, 1, 0, 4, 0, 1, 2], #5
    [0, 1, 0, 0, 1, 0, 0], #6
    [1, 2, 1, 4, 2, 0, 0]  #7
])
b = np.array([
    #5 #7 #1,3#3,1#2 #4 #6
    [0, 2, 0, 0, 1, 4, 1], #5
    [2, 0, 1, 1, 2, 4, 0], #7
    [0, 1, 0, 1, 1, 0, 0], #1,3 could be either
    [0, 1, 1, 0, 1, 0, 0], #1,3 could be either
    [1, 2, 1, 1, 0, 2, 1], #2
    [4, 4, 0, 0, 2, 0, 0], #4
    [1, 0, 0, 0, 1, 0, 0]  #6
])

Saat ini saya menguji apakah nilai eigennya sama menggunakan numpy.linalg.eigvals (Saya bahkan tidak yakin ini adalah kondisi yang memadai), tetapi saya ingin mencari tes yang tidak melibatkan akurasi numerik, karena saya berurusan dengan bilangan bulat di sini.


person C. Yduqoli    schedule 29.10.2018    source sumber
comment
Masalah ini setara dengan isomorfisme grafik: solusi eksak cenderung lambat. Lihat mis. pertanyaan ini   -  person Maxim    schedule 29.10.2018
comment
Saya bertanya-tanya bagaimana Anda membuat b tanpa mengetahui indeks [5, 7, 1, 3, 2, 4, 6] sejak awal.   -  person Andreas K.    schedule 29.10.2018
comment
Saya menghitung kemunculan objek secara bersamaan berdasarkan daftar daftar objek. Objek-objek ini mendapatkan indeks acak yang ditetapkan (di luar kendali saya) sebelum matriks kejadian bersama dibuat. Dengan cara ini matriks co-oc yang berbeda dibuat setiap saat. Sebagai contoh, saya menggunakan dua matriks ini dan menetapkan indeks untuk matriks kedua secara manual.   -  person C. Yduqoli    schedule 30.10.2018
comment
Saya sarankan Anda membaca masalah isomorfisme grafik dan memeriksa apakah jenis isomorfisme grafik Anda grafik adalah masalah yang terpecahkan. Jika tidak, Anda mungkin akan menghadapi banyak kekerasan.   -  person Daniel F    schedule 30.10.2018
comment
Jika dalam banyak kasus matriks Anda tidak ekuivalen, mungkin ada baiknya melakukan pengujian terlebih dahulu yang akan memberi tahu Anda jika matriks tersebut tidak ekuivalen, namun tidak akan memberi tahu Anda apakah matriks tersebut ekuivalen. Misalnya, untuk setiap matriks, Anda dapat menghitung multiset setiap baris (misalnya baris pertama dari a adalah {(4,0), (3,1)}) dan kemudian membentuk multiset dari multiset baris. Jika kedua multiset ini (satu untuk a, satu untuk b) tidak sama maka matriks-matriksnya tidak ekuivalen.   -  person dmuir    schedule 30.10.2018


Jawaban (3)


Berikut adalah solusi vektor berdasarkan sorting dan memanfaatkan searchsorted -

import pandas as pd

# Sort rows for a and b
aS = np.sort(a,axis=1)
bS = np.sort(b,axis=1)

# Scale down each row to a scalar each
scale = np.r_[(np.maximum(aS.max(0),bS.max(0))+1)[::-1].cumprod()[::-1][1:],1]
aS1D = aS.dot(scale)
bS1D = bS.dot(scale)

# Use searchsorted to get the correspondence on indexing
sidx = aS1D.argsort()
searchsorted_idx = np.searchsorted(aS1D,bS1D,sorter=sidx)
searchsorted_idx[searchsorted_idx==len(aS1D)] = len(aS1D)-1
df = pd.DataFrame({'A':searchsorted_idx})
new_order = sidx[df.groupby('A').cumcount().values+searchsorted_idx]
# new_order is the permuted order, i.e. [5, 7, 1, 3, 2, 4, 6]

# Finally index into a with the new_order and compare against b
out = np.array_equal(a[new_order[:,None], new_order],b)
person Divakar    schedule 29.10.2018
comment
Bagus, tapi jauh lebih lambat dibandingkan solusi dengan nilai eigen. - person Andreas K.; 29.10.2018
comment
@AndyK Berapa ukuran yang Anda uji? - person Divakar; 29.10.2018
comment
Sepertinya itu tidak benar. Saya mencoba a = scipy.linalg.toeplitz(np.arange(8)) dan b beberapa versi acaknya, dan saya mendapatkan False sepanjang waktu. - person Paul Panzer; 29.10.2018
comment
@AndyK Saya yakin nilai eigen saja tidak cukup. misalnya [[4 0] [0 0]] dan [[2 2] [2 2]] memiliki nilai eigen yang sama tetapi jelas tidak dapat diacak satu sama lain. - person Paul Panzer; 29.10.2018
comment
Bagaimana dengan np.array([[12, 8, 11],[8, 0, 12],[11, 12, 8]]) np.array([[0, 12, 8],[12, 8, 11],[8, 11, 12]]). Ini seharusnya mengembalikan True, tetapi metode Anda mengembalikan False dalam pengujian saya. - person C. Yduqoli; 30.10.2018
comment
Juga memunculkan pengecualian untuk [[12 8] [ 8 4]] [[ 6 5] [ 5 14]]: new_order = sidx[df.groupby('A').cumcount().values+searchsorted_idx] IndexError: index 2 is out of bounds for axis 1 with size 2 - person C. Yduqoli; 30.10.2018

Saya berasumsi bahwa Anda memiliki daftar permutasi baris/kolom a yang menghasilkan b, mis. sesuatu seperti ini

p = np.array([5, 7, 1, 3, 2, 4, 6]) - 1

Kemudian Anda cukup melakukan hal berikut di a

a_p = a[p]
a_p = a_p[:, p]

dan periksa apakah b dan a_p yang diijinkan sama:

(a_p == b).all()

Edit: karena Anda tidak memiliki daftar seperti p di atas, Anda dapat (setidaknya untuk array kecil a dan b) membuat permutasi indeks dan memeriksa masing-masing indeks:

from itertools import permutations

def a_p(a, b, p):
    p = np.array(p)
    a_p = a[p]
    a_p = a_p[:, p]
    return a_p

for p in permutations(range(a.shape[0])):
    if (a_p(a, b, p) == b).all():
        print('True')
        break
else:
    print('False')

Perhatikan bahwa metode brute force ini juga berfungsi untuk matriks non-simetris. Namun karena jumlah permutasi sangat besar untuk array besar a dan b, metode ini bisa sangat lambat. Jadi solusi Anda dengan menghitung nilai eigen jauh lebih baik.

Berikut patokannya:

def Yduqoli(a, b):
    ''' I suppose your solution is similar'''
    if (np.array(np.unique(a, return_counts=True)) == np.array(np.unique(b, return_counts=True))).all():
        a_eigs = np.sort(np.linalg.eigvals(a))
        b_eigs = np.sort(np.linalg.eigvals(b))
        return np.allclose(a_eigs, b_eigs)
    else:
        return False

def AndyK(a, b):
    for p in permutations(range(a.shape[0])):
        if (a_p(a, b, p) == b).all():
            return True
    return False  

%timeit AndyK(a,b)
103 ms ± 4.54 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

%timeit Yduqoli(a,b)
408 µs ± 65.4 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

dimana saya menggunakan matriks simetris a dan b yang disediakan oleh OP.

Pembaruan: Seperti yang disebutkan oleh Paul Panzer, memeriksa nilai eigen saja dapat memberikan hasil yang salah dalam beberapa kasus, misalnya. a = np.array([[4, 0], [0, 0]]), b = np.array([[2, 2], [2, 2]]) mempunyai nilai eigen yang sama, namun tidak dapat dikocok satu sama lain. Jadi pertama-tama kita perlu memeriksa apakah array a dan b memiliki elemen yang sama (terlepas dari posisinya).

person Andreas K.    schedule 29.10.2018
comment
Maaf, saya tidak punya daftar itu. - person C. Yduqoli; 29.10.2018
comment
Maksudnya a = np.array([[4, 0], [0, 0]])? Simetris karena a = a.T. Tapi itu adalah contoh yang sewenang-wenang. - person Andreas K.; 30.10.2018

Anda selalu dapat mengurutkan matriks berdasarkan norma baris dan melihat apakah matriksnya berbeda. Jika dua baris memiliki norma yang sama, Anda harus memeriksa permutasi baris yang memiliki norma yang sama. Namun hal ini mengurangi masalah menjadi hanya baris dengan norma yang sama. Dalam banyak kasus, Anda dapat mengurutkan terlebih dahulu berdasarkan 2 norma, lalu 1 norma, dan terakhir melakukan brute force pada permutasi yang tersisa.

import numpy as np

def get_row_norm(a):
    """
    Sort by 2-norm
    """
    row_norms = np.sum(a**2, axis=1)
    return row_norms

def sort(a):
    """
    Return the matrix a sorted by 2-norm
    """
    n = a.shape[0]
    # Get the norms
    row_norms = get_row_norm(a)
    # Get the order
    order = np.argsort(row_norms)[::-1]

    sorted_a = a.copy()

    for m in range(n):
        i = order[m]
        for k in range(m+1): 
            j = order[k]
            sorted_a[m, k] = a[i, j]
            sorted_a[k, m] = a[i, j]

    return sorted_a


a = np.array([
    #1 #2 #3 #4 #5 #6 #7
    [0, 1, 1, 0, 0, 0, 1], #1
    [1, 0, 1, 2, 1, 1, 2], #2
    [1, 1, 0, 0, 0, 0, 1], #3
    [0, 2, 0, 0, 4, 0, 4], #4
    [0, 1, 0, 4, 0, 1, 2], #5
    [0, 1, 0, 0, 1, 0, 0], #6
    [1, 2, 1, 4, 2, 0, 0]  #7
])  
b = np.array([
    #5 #7 #1,3#3,1#2 #4 #6 
    [0, 2, 0, 0, 1, 4, 1], #5
    [2, 0, 1, 1, 2, 4, 0], #7
    [0, 1, 0, 1, 1, 0, 0], #1,3 could be either
    [0, 1, 1, 0, 1, 0, 0], #1,3 could be either
    [1, 2, 1, 1, 0, 2, 1], #2
    [4, 4, 0, 0, 2, 0, 0], #4
    [1, 0, 0, 0, 1, 0, 0]  #6
])

# Sort a and b
A = sort(a)
B = sort(b)
# Print the norms
print(get_row_norm(a)) # [ 3. 12.  3. 36. 22.  2. 26.]
print(get_row_norm(A)) # [36. 26. 22. 12.  3.  3.  2.]
print(get_row_norm(B)) # [36. 26. 22. 12.  3.  3.  2.]
# Assert that they are equal
print( (A == B).all())

Perhatikan bahwa jika keduanya tidak sama, Anda masih harus memeriksa permutasi baris kelima dan keenam, karena normanya sama.

person user2653663    schedule 29.10.2018
comment
Berikut contoh tandingannya: a = np.array([[2, 2], [0, 1]]), b = np.array([[1, 0], [2, 2]]), dengan b adalah a dengan baris dan kolom yang ditukar. - person Andreas K.; 29.10.2018
comment
@AndyK Tapi itu matriks yang berbeda sama sekali? - person user2653663; 29.10.2018
comment
Maksud saya b adalah a dengan baris yang ditukar 0 ‹-› 1 dan kolom 0 ‹-› 1. - person Andreas K.; 29.10.2018
comment
Namun matriks tersebut tidak simetris dan itulah yang diminta oleh operasi. - person user2653663; 29.10.2018
comment
Memang benar kamu benar. Saya lupa bahwa mereka harus simetris. - person Andreas K.; 29.10.2018
comment
Bagaimana dengan [[0 1] [1 1]] [[1 1] [1 0]]? Tidak ada perbandingan yang sama setelah pengurutan, tetapi tidak ada norma baris yang diduplikasi. - person C. Yduqoli; 30.10.2018
comment
@C.Yduqoli Anda benar. Ada bug dalam satu lingkaran yang melakukan penyortiran. for k in range(m): seharusnya for k in range(m+1). Saya memperbaikinya pada kode di atas. - person user2653663; 30.10.2018