Skip to content

Commit

Permalink
Merge branch 'PR-LapackSVD' into qiskit
Browse files Browse the repository at this point in the history
  • Loading branch information
Patataman authored Aug 7, 2023
2 parents 82e4943 + 6c3ff5c commit 7813e70
Show file tree
Hide file tree
Showing 6 changed files with 199 additions and 4 deletions.
36 changes: 36 additions & 0 deletions src/framework/lapack_protos.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// Dependencies: BLAS - LAPACK

#ifndef _aer_framework_lapack_protos_hpp
#define _aer_framework_lapack_protos_hpp

#include <complex>
#include <iostream>
#include <vector>
#include <array>

#ifdef __cplusplus
extern "C" {
#endif

// LAPACK SVD function
// https://netlib.org/lapack/explore-html/d3/da8/group__complex16_g_esing_gad6f0c85f3cca2968e1ef901d2b6014ee.html
void zgesvd_(const char *jobu, const char *jobvt, const size_t *m,
const size_t *n, std::complex<double> *a, const size_t *lda,
double *s, std::complex<double> *u,
const size_t *ldu, std::complex<double> *vt,
const size_t *ldvt, std::complex<double> *work,
const size_t *lwork, double *rwork, int *info);

// D&C approach
// https://netlib.org/lapack/explore-html/d3/da8/group__complex16_g_esing_gaccb06ed106ce18814ad7069dcb43aa27.html
void zgesdd_(const char *jobz, const size_t *m, const size_t *n,
std::complex<double> *a, const size_t *lda, double *s,
std::complex<double> *u, const size_t *ldu, std::complex<double> *vt,
const size_t *ldvt, std::complex<double> *work,
const size_t *lwork, double *rwork, int *iwork, int *info);

#ifdef __cplusplus
}
#endif

#endif // end __lapack_protos_h_
Original file line number Diff line number Diff line change
Expand Up @@ -542,7 +542,9 @@ void MPS::apply_swap_internal(uint_t index_A, uint_t index_B, bool swap_gate) {
// to the left
std::swap(qubit_ordering_.order_[index_A], qubit_ordering_.order_[index_B]);
// For logging purposes:
#ifdef DEBUG
print_to_log_internal_swap(index_A, index_B);
#endif

// update qubit locations after all the swaps
for (uint_t i = 0; i < num_qubits_; i++)
Expand Down Expand Up @@ -664,8 +666,10 @@ void MPS::common_apply_2_qubit_gate(
rvector_t lambda;
double discarded_value =
MPS_Tensor::Decompose(temp, left_gamma, lambda, right_gamma);
#ifdef DEBUG
if (discarded_value > json_chop_threshold_)
MPS::print_to_log("discarded_value=", discarded_value, ", ");
#endif

if (A != 0)
left_gamma.div_Gamma_by_left_Lambda(lambda_reg_[A - 1]);
Expand Down Expand Up @@ -1786,7 +1790,12 @@ void MPS::initialize_from_matrix(uint_t num_qubits, const cmatrix_t &mat) {
if (first_iter) {
remaining_matrix = mat;
} else {
cmatrix_t temp = mul_matrix_by_lambda(V, S);
cmatrix_t temp;
if (getenv("QISKIT_LAPACK_SVD")) {
temp = mul_matrix_by_lambda(AER::Utils::dagger(V), S);
} else {
temp = mul_matrix_by_lambda(V, S);
}
remaining_matrix = AER::Utils::dagger(temp);
}
reshaped_matrix = reshape_matrix(remaining_matrix);
Expand All @@ -1811,7 +1820,12 @@ void MPS::initialize_from_matrix(uint_t num_qubits, const cmatrix_t &mat) {
first_iter = false;
}
// step 4 - create the rightmost gamma and update q_reg_
std::vector<cmatrix_t> right_data = reshape_V_after_SVD(V);
std::vector<cmatrix_t> right_data;
if (getenv("QISKIT_LAPACK_SVD")) {
right_data = reshape_VH_after_SVD(V);
} else {
right_data = reshape_V_after_SVD(V);
}

MPS_Tensor right_gamma(right_data[0], right_data[1]);
q_reg_.push_back(right_gamma);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -603,7 +603,11 @@ double MPS_Tensor::Decompose(MPS_Tensor &temp, MPS_Tensor &left_gamma,

left_gamma.data_ = reshape_U_after_SVD(U);
lambda = S;
right_gamma.data_ = reshape_V_after_SVD(V);
if (getenv("QISKIT_LAPACK_SVD")) {
right_gamma.data_ = reshape_VH_after_SVD(V);
} else {
right_gamma.data_ = reshape_V_after_SVD(V);
}
return discarded_value;
}

Expand Down
96 changes: 95 additions & 1 deletion src/simulators/matrix_product_state/svd.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,12 @@ std::vector<cmatrix_t> reshape_V_after_SVD(const cmatrix_t V) {
AER::Utils::split(AER::Utils::dagger(V), Res[0], Res[1], 1);
return Res;
}
std::vector<cmatrix_t> reshape_VH_after_SVD(const cmatrix_t V)
{
std::vector<cmatrix_t> Res(2);
AER::Utils::split(V, Res[0], Res[1], 1);
return Res;
}

//-------------------------------------------------------------
// function name: num_of_SV
Expand Down Expand Up @@ -107,7 +113,12 @@ double reduce_zeros(cmatrix_t &U, rvector_t &S, cmatrix_t &V,
}
U.resize(U.GetRows(), new_SV_num);
S.resize(new_SV_num);
V.resize(V.GetRows(), new_SV_num);
// V**H is not the same as V
if (getenv("QISKIT_LAPACK_SVD")) {
V.resize(new_SV_num, V.GetColumns());
} else {
V.resize(V.GetRows(), new_SV_num);
}

// discarded_value is the sum of the squares of the Schmidt coeffients
// that were discarded by approximation
Expand Down Expand Up @@ -520,6 +531,14 @@ status csvd(cmatrix_t &A, cmatrix_t &U, rvector_t &S, cmatrix_t &V) {
}

void csvd_wrapper(cmatrix_t &A, cmatrix_t &U, rvector_t &S, cmatrix_t &V) {
if (getenv("QISKIT_LAPACK_SVD")) {
lapack_csvd_wrapper(A, U, S, V);
} else {
qiskit_csvd_wrapper(A, U, S, V);
}
}

void qiskit_csvd_wrapper(cmatrix_t &A, cmatrix_t &U, rvector_t &S, cmatrix_t &V) {
cmatrix_t copied_A = A;
int times = 0;
#ifdef DEBUG
Expand Down Expand Up @@ -552,4 +571,79 @@ void csvd_wrapper(cmatrix_t &A, cmatrix_t &U, rvector_t &S, cmatrix_t &V) {
S[k] /= pow(mul_factor, times);
}

void lapack_csvd_wrapper(cmatrix_t &A, cmatrix_t &U, rvector_t &S, cmatrix_t &V)
{
#ifdef DEBUG
cmatrix_t tempA = A;
#endif

const size_t m = A.GetRows(), n = A.GetColumns();
const size_t min_dim = std::min(m, n);
const size_t lda = std::max(m, n);
size_t lwork = 2 * min_dim + lda;

U.resize(m, m);
V.resize(n, n);

complex_t *lapackA = A.move_to_buffer(),
*lapackU = U.move_to_buffer(),
*lapackV = V.move_to_buffer();

double lapackS[min_dim];
complex_t work[lwork];
int info;

if (strcmp(getenv("QISKIT_LAPACK_SVD"), "DC") == 0) {
int iwork[8*min_dim];
int rwork_size = std::max(
5*min_dim*min_dim + 5*min_dim,
2*m*n + 2*min_dim*min_dim + min_dim);

double *rwork = (double*)calloc(rwork_size, sizeof(double));
lwork = -1;
zgesdd_(
"A", &m, &n, lapackA, &m, lapackS,
lapackU, &m, lapackV, &n, work, &lwork,
rwork, iwork, &info);

lwork = (int)work[0].real();
complex_t *work_= (complex_t*)calloc(lwork, sizeof(complex_t));

zgesdd_(
"A", &m, &n, lapackA, &m, lapackS,
lapackU, &m, lapackV, &n, work_, &lwork,
rwork, iwork, &info);

free(rwork);
free(work_);
} else {
// Default execution follows original method
double rwork[5*min_dim] = {0.0};
zgesvd_(
"A", "A", &m, &n, lapackA, &m, lapackS,
lapackU, &m, lapackV, &n, work, &lwork,
rwork, &info);
}
A = cmatrix_t::move_from_buffer(m, n, lapackA);
U = cmatrix_t::move_from_buffer(m, m, lapackU);
V = cmatrix_t::move_from_buffer(n, n, lapackV);

S.clear();
for (int i = 0; i<min_dim; i++)
S.push_back(lapackS[i]);

#ifdef DEBUG
validate_SVD_result(tempA, U, S, V);
#endif

if (info == 0) {
return;
} else {
std::stringstream ss;
ss << " SVD failed";
throw std::runtime_error(ss.str());
}
}


} // namespace AER
10 changes: 10 additions & 0 deletions src/simulators/matrix_product_state/svd.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,12 @@

#include "framework/types.hpp"
#include "framework/utils.hpp"
#include "framework/lapack_protos.hpp"

#include <complex>
#include <vector>


#define CHOP_THRESHOLD 1e-16

namespace AER {
Expand All @@ -32,11 +35,18 @@ cmatrix_t reshape_before_SVD(std::vector<cmatrix_t> data);
std::vector<cmatrix_t> reshape_U_after_SVD(cmatrix_t U);
rvector_t reshape_S_after_SVD(rvector_t S);
std::vector<cmatrix_t> reshape_V_after_SVD(const cmatrix_t V);
std::vector<cmatrix_t> reshape_VH_after_SVD(const cmatrix_t V);
uint_t num_of_SV(rvector_t S, double threshold);
double reduce_zeros(cmatrix_t &U, rvector_t &S, cmatrix_t &V,
uint_t max_bond_dimension, double truncation_threshold);
status csvd(cmatrix_t &C, cmatrix_t &U, rvector_t &S, cmatrix_t &V);
// Entry point for the SVD calculation
void csvd_wrapper(cmatrix_t &C, cmatrix_t &U, rvector_t &S, cmatrix_t &V);
// Original qiskit call
void qiskit_csvd_wrapper(cmatrix_t &C, cmatrix_t &U, rvector_t &S, cmatrix_t &V);
// Lapack call
void lapack_csvd_wrapper(cmatrix_t &C, cmatrix_t &U, rvector_t &S, cmatrix_t &V);

void validate_SVD_result(const cmatrix_t &A, const cmatrix_t &U,
const rvector_t &S, const cmatrix_t &V);

Expand Down
37 changes: 37 additions & 0 deletions test/terra/backends/aer_simulator/test_options.py
Original file line number Diff line number Diff line change
Expand Up @@ -302,3 +302,40 @@ def test_num_qubits(self, method):
num_qubits = FakeMontreal().configuration().num_qubits
backend = AerSimulator.from_backend(FakeMontreal(), method=method)
self.assertGreaterEqual(backend.configuration().num_qubits, num_qubits)

def test_mps_svd_method(self):
"""Test env. variabe to change MPS SVD method"""
# based on test_mps_options test
import os
shots = 4000
method = "matrix_product_state"
backend_swap = self.backend(method=method)

n = 10
circuit = QuantumCircuit(n)
for times in range(2):
for i in range(0, n, 2):
circuit.unitary(random_unitary(4), [i, i + 1])
for i in range(1, n - 1):
circuit.cx(0, i)
circuit.save_statevector("sv")

result_swap = backend_swap.run(circuit, shots=shots).result()
original_sv = result_swap.data(0)["sv"]

# run with lapack svd method
os.environ["QISKIT_LAPACK_SVD"] = "1"
result_swap = backend_swap.run(circuit, shots=shots).result()
lapack_sv = result_swap.data(0)["sv"]

# run with lapack svd D&C approach
os.environ["QISKIT_LAPACK_SVD"] = "DC"
result_swap = backend_swap.run(circuit, shots=shots).result()
lapack_dc_sv = result_swap.data(0)["sv"]
os.unsetenv("QISKIT_LAPACK_SVD")

# should give the same state vector
self.assertAlmostEqual(state_fidelity(original_sv, lapack_sv), 1.0)

# should give the same state vector
self.assertAlmostEqual(state_fidelity(original_sv, lapack_dc_sv), 1.0)

0 comments on commit 7813e70

Please sign in to comment.