1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

182

183

184

185

186

187

188

189

190

191

192

193

194

195

196

197

198

199

200

201

202

203

204

205

206

207

208

209

210

211

212

213

214

215

216

217

218

219

220

221

222

223

224

225

226

227

228

229

230

231

232

233

234

235

236

237

238

239

240

241

242

243

244

245

246

247

248

249

250

251

252

253

254

255

256

257

258

259

260

261

262

263

264

265

266

267

268

269

270

271

272

273

274

275

276

277

278

279

280

281

282

283

284

285

286

287

288

289

290

291

292

293

294

295

296

297

298

299

300

301

302

303

304

305

306

307

308

309

310

311

312

313

314

315

316

317

318

319

320

321

322

323

324

325

326

327

328

329

330

331

332

333

334

335

336

337

338

339

340

341

342

343

344

345

346

347

348

349

350

351

352

353

354

355

356

357

358

359

360

361

362

363

364

365

366

367

368

369

370

371

372

373

374

375

376

377

378

379

380

381

382

383

384

385

386

387

388

389

390

391

392

393

394

395

396

397

398

399

400

401

402

403

404

405

406

407

408

409

410

411

412

413

414

415

416

417

418

419

420

421

422

423

424

425

426

427

428

429

430

431

432

433

434

435

436

437

438

439

440

441

442

443

444

445

446

447

448

449

450

451

452

453

454

455

456

457

458

459

460

461

462

463

464

465

466

467

468

469

470

471

472

473

474

475

476

477

478

479

480

481

482

483

484

485

486

487

488

489

490

491

492

493

494

495

496

497

498

499

500

501

502

503

504

505

506

507

508

509

510

511

512

513

514

515

516

517

518

519

520

521

522

523

524

525

526

527

528

529

530

531

532

533

534

535

536

537

538

539

540

541

542

543

544

545

546

547

548

549

550

551

552

553

554

555

556

557

558

559

560

561

562

563

564

565

566

567

568

569

570

571

572

573

574

575

576

577

578

579

580

581

582

583

584

585

586

587

588

589

590

591

592

593

594

595

596

597

598

599

600

601

602

603

604

605

606

607

608

609

610

611

612

613

614

615

616

617

618

619

620

621

622

623

624

625

626

627

628

629

630

631

632

633

634

635

636

637

638

639

640

641

642

643

644

645

646

647

648

649

650

651

652

653

654

655

656

657

658

659

660

661

662

663

664

665

666

667

668

669

670

671

672

673

674

675

676

677

678

679

680

681

682

683

684

685

686

687

688

689

690

691

692

693

694

695

696

697

698

699

700

701

702

703

704

705

706

707

708

709

710

711

712

713

714

715

716

717

718

719

720

721

722

723

724

725

726

727

728

729

730

731

732

733

734

735

736

737

738

739

740

741

742

743

744

745

746

747

748

749

750

751

752

753

754

755

756

757

758

759

760

761

762

763

764

765

766

767

768

769

770

771

772

773

774

775

776

777

778

779

780

781

782

783

784

785

786

787

788

789

790

791

792

793

794

795

796

797

798

799

800

801

802

803

804

805

806

807

808

809

810

811

812

813

814

815

816

817

818

819

820

821

822

823

824

825

826

827

828

829

830

831

832

833

834

835

836

837

838

839

840

841

842

843

844

845

846

847

848

849

850

851

852

853

854

855

856

857

858

859

860

861

862

863

864

865

866

867

868

869

870

871

872

873

874

875

876

877

878

879

880

881

882

883

884

885

886

887

888

889

890

891

892

893

894

895

896

897

898

899

900

901

902

903

904

905

906

907

908

909

910

911

912

913

914

915

916

917

918

919

920

921

922

923

924

925

926

927

928

929

930

931

932

933

934

935

936

937

938

939

940

941

942

943

944

945

946

947

948

949

950

951

952

953

954

955

956

957

958

959

960

961

962

963

964

965

966

967

968

969

970

971

972

973

974

975

976

977

978

979

980

981

982

983

984

985

986

987

988

989

990

991

992

993

994

995

996

997

998

999

1000

1001

1002

1003

1004

1005

1006

1007

1008

1009

1010

1011

1012

1013

1014

1015

1016

1017

1018

1019

1020

1021

1022

1023

1024

1025

1026

1027

1028

1029

1030

1031

1032

1033

1034

1035

1036

1037

1038

1039

1040

1041

1042

1043

1044

1045

1046

1047

1048

1049

1050

1051

1052

1053

1054

1055

1056

1057

1058

1059

1060

1061

1062

1063

1064

1065

1066

1067

1068

1069

1070

1071

1072

1073

1074

1075

1076

1077

1078

1079

1080

1081

1082

1083

1084

1085

1086

1087

1088

1089

1090

1091

1092

1093

1094

1095

1096

1097

1098

1099

1100

1101

1102

1103

1104

1105

1106

1107

1108

1109

1110

1111

1112

1113

1114

1115

1116

1117

1118

1119

1120

1121

1122

1123

1124

1125

1126

1127

1128

1129

1130

1131

1132

1133

1134

1135

1136

1137

1138

1139

1140

1141

1142

1143

1144

1145

1146

1147

1148

1149

1150

1151

1152

1153

1154

1155

1156

1157

1158

1159

1160

1161

1162

1163

1164

1165

1166

1167

1168

1169

1170

1171

1172

1173

1174

1175

1176

1177

1178

1179

1180

1181

1182

1183

1184

1185

1186

1187

1188

1189

1190

1191

1192

1193

1194

1195

1196

1197

1198

1199

1200

1201

1202

1203

1204

1205

1206

1207

1208

1209

1210

1211

1212

1213

1214

1215

1216

1217

1218

1219

1220

1221

1222

1223

1224

1225

1226

1227

1228

1229

1230

1231

1232

1233

1234

1235

1236

1237

1238

1239

1240

1241

1242

1243

1244

1245

1246

1247

1248

1249

1250

1251

1252

1253

1254

1255

1256

1257

1258

1259

1260

1261

1262

1263

1264

1265

1266

1267

1268

1269

1270

1271

1272

1273

1274

1275

1276

1277

1278

1279

1280

1281

1282

1283

1284

1285

1286

1287

1288

1289

1290

1291

1292

1293

1294

1295

1296

1297

1298

1299

1300

1301

1302

1303

1304

1305

1306

1307

1308

1309

1310

1311

1312

1313

1314

1315

1316

1317

1318

1319

1320

1321

1322

1323

1324

1325

1326

1327

1328

1329

1330

1331

1332

1333

1334

1335

1336

1337

1338

1339

1340

1341

1342

1343

1344

1345

1346

1347

1348

1349

1350

1351

1352

1353

1354

1355

1356

1357

1358

1359

1360

1361

1362

1363

1364

1365

1366

1367

1368

1369

1370

1371

1372

1373

1374

1375

1376

1377

1378

1379

1380

1381

1382

1383

1384

1385

1386

1387

1388

1389

1390

1391

1392

1393

1394

1395

1396

1397

1398

1399

1400

1401

1402

1403

1404

1405

1406

1407

1408

1409

1410

1411

1412

1413

1414

1415

1416

1417

1418

1419

1420

1421

1422

1423

1424

1425

1426

1427

1428

1429

1430

1431

1432

1433

1434

1435

1436

1437

1438

1439

1440

1441

1442

1443

1444

1445

1446

1447

1448

1449

1450

1451

1452

1453

1454

1455

1456

1457

1458

1459

1460

1461

1462

1463

1464

1465

1466

1467

1468

1469

1470

1471

1472

1473

1474

1475

1476

1477

1478

1479

1480

1481

1482

1483

1484

1485

1486

1487

1488

1489

1490

1491

1492

1493

1494

1495

1496

1497

1498

1499

1500

1501

1502

1503

1504

1505

1506

1507

1508

1509

1510

1511

1512

1513

1514

1515

1516

1517

1518

1519

1520

1521

1522

1523

1524

1525

1526

1527

1528

1529

1530

1531

1532

1533

1534

1535

1536

1537

1538

1539

1540

1541

1542

1543

1544

1545

1546

1547

1548

1549

1550

1551

1552

1553

1554

1555

1556

1557

1558

1559

1560

1561

1562

1563

1564

1565

1566

1567

1568

1569

1570

1571

1572

1573

1574

1575

1576

1577

1578

1579

1580

1581

1582

1583

1584

1585

1586

1587

1588

1589

1590

1591

1592

1593

1594

1595

1596

1597

1598

1599

1600

1601

1602

1603

1604

1605

1606

1607

1608

1609

1610

1611

1612

1613

1614

1615

1616

1617

1618

1619

1620

1621

1622

1623

1624

1625

1626

1627

1628

1629

1630

1631

1632

1633

1634

1635

1636

1637

1638

1639

1640

1641

1642

1643

1644

1645

1646

1647

1648

1649

1650

1651

1652

1653

1654

1655

1656

1657

1658

1659

1660

1661

1662

1663

1664

1665

1666

1667

1668

1669

1670

1671

1672

1673

1674

1675

1676

1677

1678

1679

1680

1681

1682

1683

1684

1685

1686

1687

1688

1689

1690

1691

1692

1693

1694

1695

1696

1697

1698

1699

1700

1701

1702

1703

1704

1705

1706

1707

1708

1709

1710

1711

1712

1713

1714

1715

1716

1717

1718

1719

1720

1721

1722

1723

1724

1725

1726

1727

1728

1729

1730

1731

1732

1733

1734

1735

1736

1737

1738

1739

1740

1741

1742

1743

1744

1745

1746

1747

1748

1749

1750

1751

1752

1753

1754

1755

1756

1757

1758

1759

1760

1761

1762

1763

1764

1765

1766

1767

1768

1769

1770

1771

1772

1773

1774

1775

1776

1777

1778

1779

1780

1781

1782

1783

1784

1785

1786

1787

1788

1789

1790

1791

1792

1793

1794

1795

1796

1797

1798

1799

1800

1801

1802

1803

1804

1805

1806

1807

1808

1809

1810

1811

1812

1813

1814

1815

1816

1817

1818

1819

1820

1821

1822

1823

1824

1825

1826

1827

1828

1829

1830

1831

1832

1833

1834

1835

1836

1837

1838

1839

1840

1841

1842

1843

1844

1845

1846

1847

1848

1849

1850

1851

1852

1853

1854

1855

1856

1857

1858

1859

1860

1861

1862

1863

1864

1865

""" 

Find a few eigenvectors and eigenvalues of a matrix. 

 

 

Uses ARPACK: http://www.caam.rice.edu/software/ARPACK/ 

 

""" 

# Wrapper implementation notes 

# 

# ARPACK Entry Points 

# ------------------- 

# The entry points to ARPACK are 

# - (s,d)seupd : single and double precision symmetric matrix 

# - (s,d,c,z)neupd: single,double,complex,double complex general matrix 

# This wrapper puts the *neupd (general matrix) interfaces in eigs() 

# and the *seupd (symmetric matrix) in eigsh(). 

# There is no Hermetian complex/double complex interface. 

# To find eigenvalues of a Hermetian matrix you 

# must use eigs() and not eigsh() 

# It might be desirable to handle the Hermetian case differently 

# and, for example, return real eigenvalues. 

 

# Number of eigenvalues returned and complex eigenvalues 

# ------------------------------------------------------ 

# The ARPACK nonsymmetric real and double interface (s,d)naupd return 

# eigenvalues and eigenvectors in real (float,double) arrays. 

# Since the eigenvalues and eigenvectors are, in general, complex 

# ARPACK puts the real and imaginary parts in consecutive entries 

# in real-valued arrays. This wrapper puts the real entries 

# into complex data types and attempts to return the requested eigenvalues 

# and eigenvectors. 

 

 

# Solver modes 

# ------------ 

# ARPACK and handle shifted and shift-inverse computations 

# for eigenvalues by providing a shift (sigma) and a solver. 

 

from __future__ import division, print_function, absolute_import 

 

__docformat__ = "restructuredtext en" 

 

__all__ = ['eigs', 'eigsh', 'svds', 'ArpackError', 'ArpackNoConvergence'] 

 

from . import _arpack 

import numpy as np 

import warnings 

from scipy.sparse.linalg.interface import aslinearoperator, LinearOperator 

from scipy.sparse import eye, issparse, isspmatrix, isspmatrix_csr 

from scipy.linalg import eig, eigh, lu_factor, lu_solve 

from scipy.sparse.sputils import isdense 

from scipy.sparse.linalg import gmres, splu 

from scipy._lib._util import _aligned_zeros 

from scipy._lib._threadsafety import ReentrancyLock 

 

 

_type_conv = {'f': 's', 'd': 'd', 'F': 'c', 'D': 'z'} 

_ndigits = {'f': 5, 'd': 12, 'F': 5, 'D': 12} 

 

DNAUPD_ERRORS = { 

0: "Normal exit.", 

1: "Maximum number of iterations taken. " 

"All possible eigenvalues of OP has been found. IPARAM(5) " 

"returns the number of wanted converged Ritz values.", 

2: "No longer an informational error. Deprecated starting " 

"with release 2 of ARPACK.", 

3: "No shifts could be applied during a cycle of the " 

"Implicitly restarted Arnoldi iteration. One possibility " 

"is to increase the size of NCV relative to NEV. ", 

-1: "N must be positive.", 

-2: "NEV must be positive.", 

-3: "NCV-NEV >= 2 and less than or equal to N.", 

-4: "The maximum number of Arnoldi update iterations allowed " 

"must be greater than zero.", 

-5: " WHICH must be one of 'LM', 'SM', 'LR', 'SR', 'LI', 'SI'", 

-6: "BMAT must be one of 'I' or 'G'.", 

-7: "Length of private work array WORKL is not sufficient.", 

-8: "Error return from LAPACK eigenvalue calculation;", 

-9: "Starting vector is zero.", 

-10: "IPARAM(7) must be 1,2,3,4.", 

-11: "IPARAM(7) = 1 and BMAT = 'G' are incompatible.", 

-12: "IPARAM(1) must be equal to 0 or 1.", 

-13: "NEV and WHICH = 'BE' are incompatible.", 

-9999: "Could not build an Arnoldi factorization. " 

"IPARAM(5) returns the size of the current Arnoldi " 

"factorization. The user is advised to check that " 

"enough workspace and array storage has been allocated." 

} 

 

SNAUPD_ERRORS = DNAUPD_ERRORS 

 

ZNAUPD_ERRORS = DNAUPD_ERRORS.copy() 

ZNAUPD_ERRORS[-10] = "IPARAM(7) must be 1,2,3." 

 

CNAUPD_ERRORS = ZNAUPD_ERRORS 

 

DSAUPD_ERRORS = { 

0: "Normal exit.", 

1: "Maximum number of iterations taken. " 

"All possible eigenvalues of OP has been found.", 

2: "No longer an informational error. Deprecated starting with " 

"release 2 of ARPACK.", 

3: "No shifts could be applied during a cycle of the Implicitly " 

"restarted Arnoldi iteration. One possibility is to increase " 

"the size of NCV relative to NEV. ", 

-1: "N must be positive.", 

-2: "NEV must be positive.", 

-3: "NCV must be greater than NEV and less than or equal to N.", 

-4: "The maximum number of Arnoldi update iterations allowed " 

"must be greater than zero.", 

-5: "WHICH must be one of 'LM', 'SM', 'LA', 'SA' or 'BE'.", 

-6: "BMAT must be one of 'I' or 'G'.", 

-7: "Length of private work array WORKL is not sufficient.", 

-8: "Error return from trid. eigenvalue calculation; " 

"Informational error from LAPACK routine dsteqr .", 

-9: "Starting vector is zero.", 

-10: "IPARAM(7) must be 1,2,3,4,5.", 

-11: "IPARAM(7) = 1 and BMAT = 'G' are incompatible.", 

-12: "IPARAM(1) must be equal to 0 or 1.", 

-13: "NEV and WHICH = 'BE' are incompatible. ", 

-9999: "Could not build an Arnoldi factorization. " 

"IPARAM(5) returns the size of the current Arnoldi " 

"factorization. The user is advised to check that " 

"enough workspace and array storage has been allocated.", 

} 

 

SSAUPD_ERRORS = DSAUPD_ERRORS 

 

DNEUPD_ERRORS = { 

0: "Normal exit.", 

1: "The Schur form computed by LAPACK routine dlahqr " 

"could not be reordered by LAPACK routine dtrsen. " 

"Re-enter subroutine dneupd with IPARAM(5)NCV and " 

"increase the size of the arrays DR and DI to have " 

"dimension at least dimension NCV and allocate at least NCV " 

"columns for Z. NOTE: Not necessary if Z and V share " 

"the same space. Please notify the authors if this error" 

"occurs.", 

-1: "N must be positive.", 

-2: "NEV must be positive.", 

-3: "NCV-NEV >= 2 and less than or equal to N.", 

-5: "WHICH must be one of 'LM', 'SM', 'LR', 'SR', 'LI', 'SI'", 

-6: "BMAT must be one of 'I' or 'G'.", 

-7: "Length of private work WORKL array is not sufficient.", 

-8: "Error return from calculation of a real Schur form. " 

"Informational error from LAPACK routine dlahqr .", 

-9: "Error return from calculation of eigenvectors. " 

"Informational error from LAPACK routine dtrevc.", 

-10: "IPARAM(7) must be 1,2,3,4.", 

-11: "IPARAM(7) = 1 and BMAT = 'G' are incompatible.", 

-12: "HOWMNY = 'S' not yet implemented", 

-13: "HOWMNY must be one of 'A' or 'P' if RVEC = .true.", 

-14: "DNAUPD did not find any eigenvalues to sufficient " 

"accuracy.", 

-15: "DNEUPD got a different count of the number of converged " 

"Ritz values than DNAUPD got. This indicates the user " 

"probably made an error in passing data from DNAUPD to " 

"DNEUPD or that the data was modified before entering " 

"DNEUPD", 

} 

 

SNEUPD_ERRORS = DNEUPD_ERRORS.copy() 

SNEUPD_ERRORS[1] = ("The Schur form computed by LAPACK routine slahqr " 

"could not be reordered by LAPACK routine strsen . " 

"Re-enter subroutine dneupd with IPARAM(5)=NCV and " 

"increase the size of the arrays DR and DI to have " 

"dimension at least dimension NCV and allocate at least " 

"NCV columns for Z. NOTE: Not necessary if Z and V share " 

"the same space. Please notify the authors if this error " 

"occurs.") 

SNEUPD_ERRORS[-14] = ("SNAUPD did not find any eigenvalues to sufficient " 

"accuracy.") 

SNEUPD_ERRORS[-15] = ("SNEUPD got a different count of the number of " 

"converged Ritz values than SNAUPD got. This indicates " 

"the user probably made an error in passing data from " 

"SNAUPD to SNEUPD or that the data was modified before " 

"entering SNEUPD") 

 

ZNEUPD_ERRORS = {0: "Normal exit.", 

1: "The Schur form computed by LAPACK routine csheqr " 

"could not be reordered by LAPACK routine ztrsen. " 

"Re-enter subroutine zneupd with IPARAM(5)=NCV and " 

"increase the size of the array D to have " 

"dimension at least dimension NCV and allocate at least " 

"NCV columns for Z. NOTE: Not necessary if Z and V share " 

"the same space. Please notify the authors if this error " 

"occurs.", 

-1: "N must be positive.", 

-2: "NEV must be positive.", 

-3: "NCV-NEV >= 1 and less than or equal to N.", 

-5: "WHICH must be one of 'LM', 'SM', 'LR', 'SR', 'LI', 'SI'", 

-6: "BMAT must be one of 'I' or 'G'.", 

-7: "Length of private work WORKL array is not sufficient.", 

-8: "Error return from LAPACK eigenvalue calculation. " 

"This should never happened.", 

-9: "Error return from calculation of eigenvectors. " 

"Informational error from LAPACK routine ztrevc.", 

-10: "IPARAM(7) must be 1,2,3", 

-11: "IPARAM(7) = 1 and BMAT = 'G' are incompatible.", 

-12: "HOWMNY = 'S' not yet implemented", 

-13: "HOWMNY must be one of 'A' or 'P' if RVEC = .true.", 

-14: "ZNAUPD did not find any eigenvalues to sufficient " 

"accuracy.", 

-15: "ZNEUPD got a different count of the number of " 

"converged Ritz values than ZNAUPD got. This " 

"indicates the user probably made an error in passing " 

"data from ZNAUPD to ZNEUPD or that the data was " 

"modified before entering ZNEUPD" 

} 

 

CNEUPD_ERRORS = ZNEUPD_ERRORS.copy() 

CNEUPD_ERRORS[-14] = ("CNAUPD did not find any eigenvalues to sufficient " 

"accuracy.") 

CNEUPD_ERRORS[-15] = ("CNEUPD got a different count of the number of " 

"converged Ritz values than CNAUPD got. This indicates " 

"the user probably made an error in passing data from " 

"CNAUPD to CNEUPD or that the data was modified before " 

"entering CNEUPD") 

 

DSEUPD_ERRORS = { 

0: "Normal exit.", 

-1: "N must be positive.", 

-2: "NEV must be positive.", 

-3: "NCV must be greater than NEV and less than or equal to N.", 

-5: "WHICH must be one of 'LM', 'SM', 'LA', 'SA' or 'BE'.", 

-6: "BMAT must be one of 'I' or 'G'.", 

-7: "Length of private work WORKL array is not sufficient.", 

-8: ("Error return from trid. eigenvalue calculation; " 

"Information error from LAPACK routine dsteqr."), 

-9: "Starting vector is zero.", 

-10: "IPARAM(7) must be 1,2,3,4,5.", 

-11: "IPARAM(7) = 1 and BMAT = 'G' are incompatible.", 

-12: "NEV and WHICH = 'BE' are incompatible.", 

-14: "DSAUPD did not find any eigenvalues to sufficient accuracy.", 

-15: "HOWMNY must be one of 'A' or 'S' if RVEC = .true.", 

-16: "HOWMNY = 'S' not yet implemented", 

-17: ("DSEUPD got a different count of the number of converged " 

"Ritz values than DSAUPD got. This indicates the user " 

"probably made an error in passing data from DSAUPD to " 

"DSEUPD or that the data was modified before entering " 

"DSEUPD.") 

} 

 

SSEUPD_ERRORS = DSEUPD_ERRORS.copy() 

SSEUPD_ERRORS[-14] = ("SSAUPD did not find any eigenvalues " 

"to sufficient accuracy.") 

SSEUPD_ERRORS[-17] = ("SSEUPD got a different count of the number of " 

"converged " 

"Ritz values than SSAUPD got. This indicates the user " 

"probably made an error in passing data from SSAUPD to " 

"SSEUPD or that the data was modified before entering " 

"SSEUPD.") 

 

_SAUPD_ERRORS = {'d': DSAUPD_ERRORS, 

's': SSAUPD_ERRORS} 

_NAUPD_ERRORS = {'d': DNAUPD_ERRORS, 

's': SNAUPD_ERRORS, 

'z': ZNAUPD_ERRORS, 

'c': CNAUPD_ERRORS} 

_SEUPD_ERRORS = {'d': DSEUPD_ERRORS, 

's': SSEUPD_ERRORS} 

_NEUPD_ERRORS = {'d': DNEUPD_ERRORS, 

's': SNEUPD_ERRORS, 

'z': ZNEUPD_ERRORS, 

'c': CNEUPD_ERRORS} 

 

# accepted values of parameter WHICH in _SEUPD 

_SEUPD_WHICH = ['LM', 'SM', 'LA', 'SA', 'BE'] 

 

# accepted values of parameter WHICH in _NAUPD 

_NEUPD_WHICH = ['LM', 'SM', 'LR', 'SR', 'LI', 'SI'] 

 

 

class ArpackError(RuntimeError): 

""" 

ARPACK error 

""" 

def __init__(self, info, infodict=_NAUPD_ERRORS): 

msg = infodict.get(info, "Unknown error") 

RuntimeError.__init__(self, "ARPACK error %d: %s" % (info, msg)) 

 

 

class ArpackNoConvergence(ArpackError): 

""" 

ARPACK iteration did not converge 

 

Attributes 

---------- 

eigenvalues : ndarray 

Partial result. Converged eigenvalues. 

eigenvectors : ndarray 

Partial result. Converged eigenvectors. 

 

""" 

def __init__(self, msg, eigenvalues, eigenvectors): 

ArpackError.__init__(self, -1, {-1: msg}) 

self.eigenvalues = eigenvalues 

self.eigenvectors = eigenvectors 

 

 

def choose_ncv(k): 

""" 

Choose number of lanczos vectors based on target number 

of singular/eigen values and vectors to compute, k. 

""" 

return max(2 * k + 1, 20) 

 

 

class _ArpackParams(object): 

def __init__(self, n, k, tp, mode=1, sigma=None, 

ncv=None, v0=None, maxiter=None, which="LM", tol=0): 

if k <= 0: 

raise ValueError("k must be positive, k=%d" % k) 

 

if maxiter is None: 

maxiter = n * 10 

if maxiter <= 0: 

raise ValueError("maxiter must be positive, maxiter=%d" % maxiter) 

 

if tp not in 'fdFD': 

raise ValueError("matrix type must be 'f', 'd', 'F', or 'D'") 

 

if v0 is not None: 

# ARPACK overwrites its initial resid, make a copy 

self.resid = np.array(v0, copy=True) 

info = 1 

else: 

# ARPACK will use a random initial vector. 

self.resid = np.zeros(n, tp) 

info = 0 

 

if sigma is None: 

#sigma not used 

self.sigma = 0 

else: 

self.sigma = sigma 

 

if ncv is None: 

ncv = choose_ncv(k) 

ncv = min(ncv, n) 

 

self.v = np.zeros((n, ncv), tp) # holds Ritz vectors 

self.iparam = np.zeros(11, "int") 

 

# set solver mode and parameters 

ishfts = 1 

self.mode = mode 

self.iparam[0] = ishfts 

self.iparam[2] = maxiter 

self.iparam[3] = 1 

self.iparam[6] = mode 

 

self.n = n 

self.tol = tol 

self.k = k 

self.maxiter = maxiter 

self.ncv = ncv 

self.which = which 

self.tp = tp 

self.info = info 

 

self.converged = False 

self.ido = 0 

 

def _raise_no_convergence(self): 

msg = "No convergence (%d iterations, %d/%d eigenvectors converged)" 

k_ok = self.iparam[4] 

num_iter = self.iparam[2] 

try: 

ev, vec = self.extract(True) 

except ArpackError as err: 

msg = "%s [%s]" % (msg, err) 

ev = np.zeros((0,)) 

vec = np.zeros((self.n, 0)) 

k_ok = 0 

raise ArpackNoConvergence(msg % (num_iter, k_ok, self.k), ev, vec) 

 

 

class _SymmetricArpackParams(_ArpackParams): 

def __init__(self, n, k, tp, matvec, mode=1, M_matvec=None, 

Minv_matvec=None, sigma=None, 

ncv=None, v0=None, maxiter=None, which="LM", tol=0): 

# The following modes are supported: 

# mode = 1: 

# Solve the standard eigenvalue problem: 

# A*x = lambda*x : 

# A - symmetric 

# Arguments should be 

# matvec = left multiplication by A 

# M_matvec = None [not used] 

# Minv_matvec = None [not used] 

# 

# mode = 2: 

# Solve the general eigenvalue problem: 

# A*x = lambda*M*x 

# A - symmetric 

# M - symmetric positive definite 

# Arguments should be 

# matvec = left multiplication by A 

# M_matvec = left multiplication by M 

# Minv_matvec = left multiplication by M^-1 

# 

# mode = 3: 

# Solve the general eigenvalue problem in shift-invert mode: 

# A*x = lambda*M*x 

# A - symmetric 

# M - symmetric positive semi-definite 

# Arguments should be 

# matvec = None [not used] 

# M_matvec = left multiplication by M 

# or None, if M is the identity 

# Minv_matvec = left multiplication by [A-sigma*M]^-1 

# 

# mode = 4: 

# Solve the general eigenvalue problem in Buckling mode: 

# A*x = lambda*AG*x 

# A - symmetric positive semi-definite 

# AG - symmetric indefinite 

# Arguments should be 

# matvec = left multiplication by A 

# M_matvec = None [not used] 

# Minv_matvec = left multiplication by [A-sigma*AG]^-1 

# 

# mode = 5: 

# Solve the general eigenvalue problem in Cayley-transformed mode: 

# A*x = lambda*M*x 

# A - symmetric 

# M - symmetric positive semi-definite 

# Arguments should be 

# matvec = left multiplication by A 

# M_matvec = left multiplication by M 

# or None, if M is the identity 

# Minv_matvec = left multiplication by [A-sigma*M]^-1 

if mode == 1: 

if matvec is None: 

raise ValueError("matvec must be specified for mode=1") 

if M_matvec is not None: 

raise ValueError("M_matvec cannot be specified for mode=1") 

if Minv_matvec is not None: 

raise ValueError("Minv_matvec cannot be specified for mode=1") 

 

self.OP = matvec 

self.B = lambda x: x 

self.bmat = 'I' 

elif mode == 2: 

if matvec is None: 

raise ValueError("matvec must be specified for mode=2") 

if M_matvec is None: 

raise ValueError("M_matvec must be specified for mode=2") 

if Minv_matvec is None: 

raise ValueError("Minv_matvec must be specified for mode=2") 

 

self.OP = lambda x: Minv_matvec(matvec(x)) 

self.OPa = Minv_matvec 

self.OPb = matvec 

self.B = M_matvec 

self.bmat = 'G' 

elif mode == 3: 

if matvec is not None: 

raise ValueError("matvec must not be specified for mode=3") 

if Minv_matvec is None: 

raise ValueError("Minv_matvec must be specified for mode=3") 

 

if M_matvec is None: 

self.OP = Minv_matvec 

self.OPa = Minv_matvec 

self.B = lambda x: x 

self.bmat = 'I' 

else: 

self.OP = lambda x: Minv_matvec(M_matvec(x)) 

self.OPa = Minv_matvec 

self.B = M_matvec 

self.bmat = 'G' 

elif mode == 4: 

if matvec is None: 

raise ValueError("matvec must be specified for mode=4") 

if M_matvec is not None: 

raise ValueError("M_matvec must not be specified for mode=4") 

if Minv_matvec is None: 

raise ValueError("Minv_matvec must be specified for mode=4") 

self.OPa = Minv_matvec 

self.OP = lambda x: self.OPa(matvec(x)) 

self.B = matvec 

self.bmat = 'G' 

elif mode == 5: 

if matvec is None: 

raise ValueError("matvec must be specified for mode=5") 

if Minv_matvec is None: 

raise ValueError("Minv_matvec must be specified for mode=5") 

 

self.OPa = Minv_matvec 

self.A_matvec = matvec 

 

if M_matvec is None: 

self.OP = lambda x: Minv_matvec(matvec(x) + sigma * x) 

self.B = lambda x: x 

self.bmat = 'I' 

else: 

self.OP = lambda x: Minv_matvec(matvec(x) 

+ sigma * M_matvec(x)) 

self.B = M_matvec 

self.bmat = 'G' 

else: 

raise ValueError("mode=%i not implemented" % mode) 

 

if which not in _SEUPD_WHICH: 

raise ValueError("which must be one of %s" 

% ' '.join(_SEUPD_WHICH)) 

if k >= n: 

raise ValueError("k must be less than ndim(A), k=%d" % k) 

 

_ArpackParams.__init__(self, n, k, tp, mode, sigma, 

ncv, v0, maxiter, which, tol) 

 

if self.ncv > n or self.ncv <= k: 

raise ValueError("ncv must be k<ncv<=n, ncv=%s" % self.ncv) 

 

# Use _aligned_zeros to work around a f2py bug in Numpy 1.9.1 

self.workd = _aligned_zeros(3 * n, self.tp) 

self.workl = _aligned_zeros(self.ncv * (self.ncv + 8), self.tp) 

 

ltr = _type_conv[self.tp] 

if ltr not in ["s", "d"]: 

raise ValueError("Input matrix is not real-valued.") 

 

self._arpack_solver = _arpack.__dict__[ltr + 'saupd'] 

self._arpack_extract = _arpack.__dict__[ltr + 'seupd'] 

 

self.iterate_infodict = _SAUPD_ERRORS[ltr] 

self.extract_infodict = _SEUPD_ERRORS[ltr] 

 

self.ipntr = np.zeros(11, "int") 

 

def iterate(self): 

self.ido, self.tol, self.resid, self.v, self.iparam, self.ipntr, self.info = \ 

self._arpack_solver(self.ido, self.bmat, self.which, self.k, 

self.tol, self.resid, self.v, self.iparam, 

self.ipntr, self.workd, self.workl, self.info) 

 

xslice = slice(self.ipntr[0] - 1, self.ipntr[0] - 1 + self.n) 

yslice = slice(self.ipntr[1] - 1, self.ipntr[1] - 1 + self.n) 

if self.ido == -1: 

# initialization 

self.workd[yslice] = self.OP(self.workd[xslice]) 

elif self.ido == 1: 

# compute y = Op*x 

if self.mode == 1: 

self.workd[yslice] = self.OP(self.workd[xslice]) 

elif self.mode == 2: 

self.workd[xslice] = self.OPb(self.workd[xslice]) 

self.workd[yslice] = self.OPa(self.workd[xslice]) 

elif self.mode == 5: 

Bxslice = slice(self.ipntr[2] - 1, self.ipntr[2] - 1 + self.n) 

Ax = self.A_matvec(self.workd[xslice]) 

self.workd[yslice] = self.OPa(Ax + (self.sigma * 

self.workd[Bxslice])) 

else: 

Bxslice = slice(self.ipntr[2] - 1, self.ipntr[2] - 1 + self.n) 

self.workd[yslice] = self.OPa(self.workd[Bxslice]) 

elif self.ido == 2: 

self.workd[yslice] = self.B(self.workd[xslice]) 

elif self.ido == 3: 

raise ValueError("ARPACK requested user shifts. Assure ISHIFT==0") 

else: 

self.converged = True 

 

if self.info == 0: 

pass 

elif self.info == 1: 

self._raise_no_convergence() 

else: 

raise ArpackError(self.info, infodict=self.iterate_infodict) 

 

def extract(self, return_eigenvectors): 

rvec = return_eigenvectors 

ierr = 0 

howmny = 'A' # return all eigenvectors 

sselect = np.zeros(self.ncv, 'int') # unused 

d, z, ierr = self._arpack_extract(rvec, howmny, sselect, self.sigma, 

self.bmat, self.which, self.k, 

self.tol, self.resid, self.v, 

self.iparam[0:7], self.ipntr, 

self.workd[0:2 * self.n], 

self.workl, ierr) 

if ierr != 0: 

raise ArpackError(ierr, infodict=self.extract_infodict) 

k_ok = self.iparam[4] 

d = d[:k_ok] 

z = z[:, :k_ok] 

 

if return_eigenvectors: 

return d, z 

else: 

return d 

 

 

class _UnsymmetricArpackParams(_ArpackParams): 

def __init__(self, n, k, tp, matvec, mode=1, M_matvec=None, 

Minv_matvec=None, sigma=None, 

ncv=None, v0=None, maxiter=None, which="LM", tol=0): 

# The following modes are supported: 

# mode = 1: 

# Solve the standard eigenvalue problem: 

# A*x = lambda*x 

# A - square matrix 

# Arguments should be 

# matvec = left multiplication by A 

# M_matvec = None [not used] 

# Minv_matvec = None [not used] 

# 

# mode = 2: 

# Solve the generalized eigenvalue problem: 

# A*x = lambda*M*x 

# A - square matrix 

# M - symmetric, positive semi-definite 

# Arguments should be 

# matvec = left multiplication by A 

# M_matvec = left multiplication by M 

# Minv_matvec = left multiplication by M^-1 

# 

# mode = 3,4: 

# Solve the general eigenvalue problem in shift-invert mode: 

# A*x = lambda*M*x 

# A - square matrix 

# M - symmetric, positive semi-definite 

# Arguments should be 

# matvec = None [not used] 

# M_matvec = left multiplication by M 

# or None, if M is the identity 

# Minv_matvec = left multiplication by [A-sigma*M]^-1 

# if A is real and mode==3, use the real part of Minv_matvec 

# if A is real and mode==4, use the imag part of Minv_matvec 

# if A is complex and mode==3, 

# use real and imag parts of Minv_matvec 

if mode == 1: 

if matvec is None: 

raise ValueError("matvec must be specified for mode=1") 

if M_matvec is not None: 

raise ValueError("M_matvec cannot be specified for mode=1") 

if Minv_matvec is not None: 

raise ValueError("Minv_matvec cannot be specified for mode=1") 

 

self.OP = matvec 

self.B = lambda x: x 

self.bmat = 'I' 

elif mode == 2: 

if matvec is None: 

raise ValueError("matvec must be specified for mode=2") 

if M_matvec is None: 

raise ValueError("M_matvec must be specified for mode=2") 

if Minv_matvec is None: 

raise ValueError("Minv_matvec must be specified for mode=2") 

 

self.OP = lambda x: Minv_matvec(matvec(x)) 

self.OPa = Minv_matvec 

self.OPb = matvec 

self.B = M_matvec 

self.bmat = 'G' 

elif mode in (3, 4): 

if matvec is None: 

raise ValueError("matvec must be specified " 

"for mode in (3,4)") 

if Minv_matvec is None: 

raise ValueError("Minv_matvec must be specified " 

"for mode in (3,4)") 

 

self.matvec = matvec 

if tp in 'DF': # complex type 

if mode == 3: 

self.OPa = Minv_matvec 

else: 

raise ValueError("mode=4 invalid for complex A") 

else: # real type 

if mode == 3: 

self.OPa = lambda x: np.real(Minv_matvec(x)) 

else: 

self.OPa = lambda x: np.imag(Minv_matvec(x)) 

if M_matvec is None: 

self.B = lambda x: x 

self.bmat = 'I' 

self.OP = self.OPa 

else: 

self.B = M_matvec 

self.bmat = 'G' 

self.OP = lambda x: self.OPa(M_matvec(x)) 

else: 

raise ValueError("mode=%i not implemented" % mode) 

 

if which not in _NEUPD_WHICH: 

raise ValueError("Parameter which must be one of %s" 

% ' '.join(_NEUPD_WHICH)) 

if k >= n - 1: 

raise ValueError("k must be less than ndim(A)-1, k=%d" % k) 

 

_ArpackParams.__init__(self, n, k, tp, mode, sigma, 

ncv, v0, maxiter, which, tol) 

 

if self.ncv > n or self.ncv <= k + 1: 

raise ValueError("ncv must be k+1<ncv<=n, ncv=%s" % self.ncv) 

 

# Use _aligned_zeros to work around a f2py bug in Numpy 1.9.1 

self.workd = _aligned_zeros(3 * n, self.tp) 

self.workl = _aligned_zeros(3 * self.ncv * (self.ncv + 2), self.tp) 

 

ltr = _type_conv[self.tp] 

self._arpack_solver = _arpack.__dict__[ltr + 'naupd'] 

self._arpack_extract = _arpack.__dict__[ltr + 'neupd'] 

 

self.iterate_infodict = _NAUPD_ERRORS[ltr] 

self.extract_infodict = _NEUPD_ERRORS[ltr] 

 

self.ipntr = np.zeros(14, "int") 

 

if self.tp in 'FD': 

# Use _aligned_zeros to work around a f2py bug in Numpy 1.9.1 

self.rwork = _aligned_zeros(self.ncv, self.tp.lower()) 

else: 

self.rwork = None 

 

def iterate(self): 

if self.tp in 'fd': 

self.ido, self.tol, self.resid, self.v, self.iparam, self.ipntr, self.info =\ 

self._arpack_solver(self.ido, self.bmat, self.which, self.k, 

self.tol, self.resid, self.v, self.iparam, 

self.ipntr, self.workd, self.workl, 

self.info) 

else: 

self.ido, self.tol, self.resid, self.v, self.iparam, self.ipntr, self.info =\ 

self._arpack_solver(self.ido, self.bmat, self.which, self.k, 

self.tol, self.resid, self.v, self.iparam, 

self.ipntr, self.workd, self.workl, 

self.rwork, self.info) 

 

xslice = slice(self.ipntr[0] - 1, self.ipntr[0] - 1 + self.n) 

yslice = slice(self.ipntr[1] - 1, self.ipntr[1] - 1 + self.n) 

if self.ido == -1: 

# initialization 

self.workd[yslice] = self.OP(self.workd[xslice]) 

elif self.ido == 1: 

# compute y = Op*x 

if self.mode in (1, 2): 

self.workd[yslice] = self.OP(self.workd[xslice]) 

else: 

Bxslice = slice(self.ipntr[2] - 1, self.ipntr[2] - 1 + self.n) 

self.workd[yslice] = self.OPa(self.workd[Bxslice]) 

elif self.ido == 2: 

self.workd[yslice] = self.B(self.workd[xslice]) 

elif self.ido == 3: 

raise ValueError("ARPACK requested user shifts. Assure ISHIFT==0") 

else: 

self.converged = True 

 

if self.info == 0: 

pass 

elif self.info == 1: 

self._raise_no_convergence() 

else: 

raise ArpackError(self.info, infodict=self.iterate_infodict) 

 

def extract(self, return_eigenvectors): 

k, n = self.k, self.n 

 

ierr = 0 

howmny = 'A' # return all eigenvectors 

sselect = np.zeros(self.ncv, 'int') # unused 

sigmar = np.real(self.sigma) 

sigmai = np.imag(self.sigma) 

workev = np.zeros(3 * self.ncv, self.tp) 

 

if self.tp in 'fd': 

dr = np.zeros(k + 1, self.tp) 

di = np.zeros(k + 1, self.tp) 

zr = np.zeros((n, k + 1), self.tp) 

dr, di, zr, ierr = \ 

self._arpack_extract(return_eigenvectors, 

howmny, sselect, sigmar, sigmai, workev, 

self.bmat, self.which, k, self.tol, self.resid, 

self.v, self.iparam, self.ipntr, 

self.workd, self.workl, self.info) 

if ierr != 0: 

raise ArpackError(ierr, infodict=self.extract_infodict) 

nreturned = self.iparam[4] # number of good eigenvalues returned 

 

# Build complex eigenvalues from real and imaginary parts 

d = dr + 1.0j * di 

 

# Arrange the eigenvectors: complex eigenvectors are stored as 

# real,imaginary in consecutive columns 

z = zr.astype(self.tp.upper()) 

 

# The ARPACK nonsymmetric real and double interface (s,d)naupd 

# return eigenvalues and eigenvectors in real (float,double) 

# arrays. 

 

# Efficiency: this should check that return_eigenvectors == True 

# before going through this construction. 

if sigmai == 0: 

i = 0 

while i <= k: 

# check if complex 

if abs(d[i].imag) != 0: 

# this is a complex conjugate pair with eigenvalues 

# in consecutive columns 

if i < k: 

z[:, i] = zr[:, i] + 1.0j * zr[:, i + 1] 

z[:, i + 1] = z[:, i].conjugate() 

i += 1 

else: 

#last eigenvalue is complex: the imaginary part of 

# the eigenvector has not been returned 

#this can only happen if nreturned > k, so we'll 

# throw out this case. 

nreturned -= 1 

i += 1 

 

else: 

# real matrix, mode 3 or 4, imag(sigma) is nonzero: 

# see remark 3 in <s,d>neupd.f 

# Build complex eigenvalues from real and imaginary parts 

i = 0 

while i <= k: 

if abs(d[i].imag) == 0: 

d[i] = np.dot(zr[:, i], self.matvec(zr[:, i])) 

else: 

if i < k: 

z[:, i] = zr[:, i] + 1.0j * zr[:, i + 1] 

z[:, i + 1] = z[:, i].conjugate() 

d[i] = ((np.dot(zr[:, i], 

self.matvec(zr[:, i])) 

+ np.dot(zr[:, i + 1], 

self.matvec(zr[:, i + 1]))) 

+ 1j * (np.dot(zr[:, i], 

self.matvec(zr[:, i + 1])) 

- np.dot(zr[:, i + 1], 

self.matvec(zr[:, i])))) 

d[i + 1] = d[i].conj() 

i += 1 

else: 

#last eigenvalue is complex: the imaginary part of 

# the eigenvector has not been returned 

#this can only happen if nreturned > k, so we'll 

# throw out this case. 

nreturned -= 1 

i += 1 

 

# Now we have k+1 possible eigenvalues and eigenvectors 

# Return the ones specified by the keyword "which" 

 

if nreturned <= k: 

# we got less or equal as many eigenvalues we wanted 

d = d[:nreturned] 

z = z[:, :nreturned] 

else: 

# we got one extra eigenvalue (likely a cc pair, but which?) 

# cut at approx precision for sorting 

rd = np.round(d, decimals=_ndigits[self.tp]) 

if self.which in ['LR', 'SR']: 

ind = np.argsort(rd.real) 

elif self.which in ['LI', 'SI']: 

# for LI,SI ARPACK returns largest,smallest 

# abs(imaginary) why? 

ind = np.argsort(abs(rd.imag)) 

else: 

ind = np.argsort(abs(rd)) 

if self.which in ['LR', 'LM', 'LI']: 

d = d[ind[-k:]] 

z = z[:, ind[-k:]] 

if self.which in ['SR', 'SM', 'SI']: 

d = d[ind[:k]] 

z = z[:, ind[:k]] 

else: 

# complex is so much simpler... 

d, z, ierr =\ 

self._arpack_extract(return_eigenvectors, 

howmny, sselect, self.sigma, workev, 

self.bmat, self.which, k, self.tol, self.resid, 

self.v, self.iparam, self.ipntr, 

self.workd, self.workl, self.rwork, ierr) 

 

if ierr != 0: 

raise ArpackError(ierr, infodict=self.extract_infodict) 

 

k_ok = self.iparam[4] 

d = d[:k_ok] 

z = z[:, :k_ok] 

 

if return_eigenvectors: 

return d, z 

else: 

return d 

 

 

def _aslinearoperator_with_dtype(m): 

m = aslinearoperator(m) 

if not hasattr(m, 'dtype'): 

x = np.zeros(m.shape[1]) 

m.dtype = (m * x).dtype 

return m 

 

 

class SpLuInv(LinearOperator): 

""" 

SpLuInv: 

helper class to repeatedly solve M*x=b 

using a sparse LU-decopposition of M 

""" 

def __init__(self, M): 

self.M_lu = splu(M) 

self.shape = M.shape 

self.dtype = M.dtype 

self.isreal = not np.issubdtype(self.dtype, np.complexfloating) 

 

def _matvec(self, x): 

# careful here: splu.solve will throw away imaginary 

# part of x if M is real 

x = np.asarray(x) 

if self.isreal and np.issubdtype(x.dtype, np.complexfloating): 

return (self.M_lu.solve(np.real(x).astype(self.dtype)) 

+ 1j * self.M_lu.solve(np.imag(x).astype(self.dtype))) 

else: 

return self.M_lu.solve(x.astype(self.dtype)) 

 

 

class LuInv(LinearOperator): 

""" 

LuInv: 

helper class to repeatedly solve M*x=b 

using an LU-decomposition of M 

""" 

def __init__(self, M): 

self.M_lu = lu_factor(M) 

self.shape = M.shape 

self.dtype = M.dtype 

 

def _matvec(self, x): 

return lu_solve(self.M_lu, x) 

 

 

def gmres_loose(A, b, tol): 

""" 

gmres with looser termination condition. 

""" 

b = np.asarray(b) 

min_tol = 1000 * np.sqrt(b.size) * np.finfo(b.dtype).eps 

return gmres(A, b, tol=max(tol, min_tol), atol=0) 

 

 

class IterInv(LinearOperator): 

""" 

IterInv: 

helper class to repeatedly solve M*x=b 

using an iterative method. 

""" 

def __init__(self, M, ifunc=gmres_loose, tol=0): 

self.M = M 

if hasattr(M, 'dtype'): 

self.dtype = M.dtype 

else: 

x = np.zeros(M.shape[1]) 

self.dtype = (M * x).dtype 

self.shape = M.shape 

 

if tol <= 0: 

# when tol=0, ARPACK uses machine tolerance as calculated 

# by LAPACK's _LAMCH function. We should match this 

tol = 2 * np.finfo(self.dtype).eps 

self.ifunc = ifunc 

self.tol = tol 

 

def _matvec(self, x): 

b, info = self.ifunc(self.M, x, tol=self.tol) 

if info != 0: 

raise ValueError("Error in inverting M: function " 

"%s did not converge (info = %i)." 

% (self.ifunc.__name__, info)) 

return b 

 

 

class IterOpInv(LinearOperator): 

""" 

IterOpInv: 

helper class to repeatedly solve [A-sigma*M]*x = b 

using an iterative method 

""" 

def __init__(self, A, M, sigma, ifunc=gmres_loose, tol=0): 

self.A = A 

self.M = M 

self.sigma = sigma 

 

def mult_func(x): 

return A.matvec(x) - sigma * M.matvec(x) 

 

def mult_func_M_None(x): 

return A.matvec(x) - sigma * x 

 

x = np.zeros(A.shape[1]) 

if M is None: 

dtype = mult_func_M_None(x).dtype 

self.OP = LinearOperator(self.A.shape, 

mult_func_M_None, 

dtype=dtype) 

else: 

dtype = mult_func(x).dtype 

self.OP = LinearOperator(self.A.shape, 

mult_func, 

dtype=dtype) 

self.shape = A.shape 

 

if tol <= 0: 

# when tol=0, ARPACK uses machine tolerance as calculated 

# by LAPACK's _LAMCH function. We should match this 

tol = 2 * np.finfo(self.OP.dtype).eps 

self.ifunc = ifunc 

self.tol = tol 

 

def _matvec(self, x): 

b, info = self.ifunc(self.OP, x, tol=self.tol) 

if info != 0: 

raise ValueError("Error in inverting [A-sigma*M]: function " 

"%s did not converge (info = %i)." 

% (self.ifunc.__name__, info)) 

return b 

 

@property 

def dtype(self): 

return self.OP.dtype 

 

 

def get_inv_matvec(M, symmetric=False, tol=0): 

if isdense(M): 

return LuInv(M).matvec 

elif isspmatrix(M): 

if isspmatrix_csr(M) and symmetric: 

M = M.T 

return SpLuInv(M).matvec 

else: 

return IterInv(M, tol=tol).matvec 

 

 

def get_OPinv_matvec(A, M, sigma, symmetric=False, tol=0): 

if sigma == 0: 

return get_inv_matvec(A, symmetric=symmetric, tol=tol) 

 

if M is None: 

#M is the identity matrix 

if isdense(A): 

if (np.issubdtype(A.dtype, np.complexfloating) 

or np.imag(sigma) == 0): 

A = np.copy(A) 

else: 

A = A + 0j 

A.flat[::A.shape[1] + 1] -= sigma 

return LuInv(A).matvec 

elif isspmatrix(A): 

A = A - sigma * eye(A.shape[0]) 

if symmetric and isspmatrix_csr(A): 

A = A.T 

return SpLuInv(A.tocsc()).matvec 

else: 

return IterOpInv(_aslinearoperator_with_dtype(A), 

M, sigma, tol=tol).matvec 

else: 

if ((not isdense(A) and not isspmatrix(A)) or 

(not isdense(M) and not isspmatrix(M))): 

return IterOpInv(_aslinearoperator_with_dtype(A), 

_aslinearoperator_with_dtype(M), 

sigma, tol=tol).matvec 

elif isdense(A) or isdense(M): 

return LuInv(A - sigma * M).matvec 

else: 

OP = A - sigma * M 

if symmetric and isspmatrix_csr(OP): 

OP = OP.T 

return SpLuInv(OP.tocsc()).matvec 

 

 

# ARPACK is not threadsafe or reentrant (SAVE variables), so we need a 

# lock and a re-entering check. 

_ARPACK_LOCK = ReentrancyLock("Nested calls to eigs/eighs not allowed: " 

"ARPACK is not re-entrant") 

 

 

def eigs(A, k=6, M=None, sigma=None, which='LM', v0=None, 

ncv=None, maxiter=None, tol=0, return_eigenvectors=True, 

Minv=None, OPinv=None, OPpart=None): 

""" 

Find k eigenvalues and eigenvectors of the square matrix A. 

 

Solves ``A * x[i] = w[i] * x[i]``, the standard eigenvalue problem 

for w[i] eigenvalues with corresponding eigenvectors x[i]. 

 

If M is specified, solves ``A * x[i] = w[i] * M * x[i]``, the 

generalized eigenvalue problem for w[i] eigenvalues 

with corresponding eigenvectors x[i] 

 

Parameters 

---------- 

A : ndarray, sparse matrix or LinearOperator 

An array, sparse matrix, or LinearOperator representing 

the operation ``A * x``, where A is a real or complex square matrix. 

k : int, optional 

The number of eigenvalues and eigenvectors desired. 

`k` must be smaller than N-1. It is not possible to compute all 

eigenvectors of a matrix. 

M : ndarray, sparse matrix or LinearOperator, optional 

An array, sparse matrix, or LinearOperator representing 

the operation M*x for the generalized eigenvalue problem 

 

A * x = w * M * x. 

 

M must represent a real, symmetric matrix if A is real, and must 

represent a complex, hermitian matrix if A is complex. For best 

results, the data type of M should be the same as that of A. 

Additionally: 

 

If `sigma` is None, M is positive definite 

 

If sigma is specified, M is positive semi-definite 

 

If sigma is None, eigs requires an operator to compute the solution 

of the linear equation ``M * x = b``. This is done internally via a 

(sparse) LU decomposition for an explicit matrix M, or via an 

iterative solver for a general linear operator. Alternatively, 

the user can supply the matrix or operator Minv, which gives 

``x = Minv * b = M^-1 * b``. 

sigma : real or complex, optional 

Find eigenvalues near sigma using shift-invert mode. This requires 

an operator to compute the solution of the linear system 

``[A - sigma * M] * x = b``, where M is the identity matrix if 

unspecified. This is computed internally via a (sparse) LU 

decomposition for explicit matrices A & M, or via an iterative 

solver if either A or M is a general linear operator. 

Alternatively, the user can supply the matrix or operator OPinv, 

which gives ``x = OPinv * b = [A - sigma * M]^-1 * b``. 

For a real matrix A, shift-invert can either be done in imaginary 

mode or real mode, specified by the parameter OPpart ('r' or 'i'). 

Note that when sigma is specified, the keyword 'which' (below) 

refers to the shifted eigenvalues ``w'[i]`` where: 

 

If A is real and OPpart == 'r' (default), 

``w'[i] = 1/2 * [1/(w[i]-sigma) + 1/(w[i]-conj(sigma))]``. 

 

If A is real and OPpart == 'i', 

``w'[i] = 1/2i * [1/(w[i]-sigma) - 1/(w[i]-conj(sigma))]``. 

 

If A is complex, ``w'[i] = 1/(w[i]-sigma)``. 

 

v0 : ndarray, optional 

Starting vector for iteration. 

Default: random 

ncv : int, optional 

The number of Lanczos vectors generated 

`ncv` must be greater than `k`; it is recommended that ``ncv > 2*k``. 

Default: ``min(n, max(2*k + 1, 20))`` 

which : str, ['LM' | 'SM' | 'LR' | 'SR' | 'LI' | 'SI'], optional 

Which `k` eigenvectors and eigenvalues to find: 

 

'LM' : largest magnitude 

 

'SM' : smallest magnitude 

 

'LR' : largest real part 

 

'SR' : smallest real part 

 

'LI' : largest imaginary part 

 

'SI' : smallest imaginary part 

 

When sigma != None, 'which' refers to the shifted eigenvalues w'[i] 

(see discussion in 'sigma', above). ARPACK is generally better 

at finding large values than small values. If small eigenvalues are 

desired, consider using shift-invert mode for better performance. 

maxiter : int, optional 

Maximum number of Arnoldi update iterations allowed 

Default: ``n*10`` 

tol : float, optional 

Relative accuracy for eigenvalues (stopping criterion) 

The default value of 0 implies machine precision. 

return_eigenvectors : bool, optional 

Return eigenvectors (True) in addition to eigenvalues 

Minv : ndarray, sparse matrix or LinearOperator, optional 

See notes in M, above. 

OPinv : ndarray, sparse matrix or LinearOperator, optional 

See notes in sigma, above. 

OPpart : {'r' or 'i'}, optional 

See notes in sigma, above 

 

Returns 

------- 

w : ndarray 

Array of k eigenvalues. 

v : ndarray 

An array of `k` eigenvectors. 

``v[:, i]`` is the eigenvector corresponding to the eigenvalue w[i]. 

 

Raises 

------ 

ArpackNoConvergence 

When the requested convergence is not obtained. 

The currently converged eigenvalues and eigenvectors can be found 

as ``eigenvalues`` and ``eigenvectors`` attributes of the exception 

object. 

 

See Also 

-------- 

eigsh : eigenvalues and eigenvectors for symmetric matrix A 

svds : singular value decomposition for a matrix A 

 

Notes 

----- 

This function is a wrapper to the ARPACK [1]_ SNEUPD, DNEUPD, CNEUPD, 

ZNEUPD, functions which use the Implicitly Restarted Arnoldi Method to 

find the eigenvalues and eigenvectors [2]_. 

 

References 

---------- 

.. [1] ARPACK Software, http://www.caam.rice.edu/software/ARPACK/ 

.. [2] R. B. Lehoucq, D. C. Sorensen, and C. Yang, ARPACK USERS GUIDE: 

Solution of Large Scale Eigenvalue Problems by Implicitly Restarted 

Arnoldi Methods. SIAM, Philadelphia, PA, 1998. 

 

Examples 

-------- 

Find 6 eigenvectors of the identity matrix: 

 

>>> import scipy.sparse as sparse 

>>> id = np.eye(13) 

>>> vals, vecs = sparse.linalg.eigs(id, k=6) 

>>> vals 

array([ 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j]) 

>>> vecs.shape 

(13, 6) 

 

""" 

if A.shape[0] != A.shape[1]: 

raise ValueError('expected square matrix (shape=%s)' % (A.shape,)) 

if M is not None: 

if M.shape != A.shape: 

raise ValueError('wrong M dimensions %s, should be %s' 

% (M.shape, A.shape)) 

if np.dtype(M.dtype).char.lower() != np.dtype(A.dtype).char.lower(): 

warnings.warn('M does not have the same type precision as A. ' 

'This may adversely affect ARPACK convergence') 

 

n = A.shape[0] 

 

if k <= 0: 

raise ValueError("k=%d must be greater than 0." % k) 

 

if k >= n - 1: 

warnings.warn("k >= N - 1 for N * N square matrix. " 

"Attempting to use scipy.linalg.eig instead.", 

RuntimeWarning) 

 

if issparse(A): 

raise TypeError("Cannot use scipy.linalg.eig for sparse A with " 

"k >= N - 1. Use scipy.linalg.eig(A.toarray()) or" 

" reduce k.") 

if isinstance(A, LinearOperator): 

raise TypeError("Cannot use scipy.linalg.eig for LinearOperator " 

"A with k >= N - 1.") 

if isinstance(M, LinearOperator): 

raise TypeError("Cannot use scipy.linalg.eig for LinearOperator " 

"M with k >= N - 1.") 

 

return eig(A, b=M, right=return_eigenvectors) 

 

if sigma is None: 

matvec = _aslinearoperator_with_dtype(A).matvec 

 

if OPinv is not None: 

raise ValueError("OPinv should not be specified " 

"with sigma = None.") 

if OPpart is not None: 

raise ValueError("OPpart should not be specified with " 

"sigma = None or complex A") 

 

if M is None: 

#standard eigenvalue problem 

mode = 1 

M_matvec = None 

Minv_matvec = None 

if Minv is not None: 

raise ValueError("Minv should not be " 

"specified with M = None.") 

else: 

#general eigenvalue problem 

mode = 2 

if Minv is None: 

Minv_matvec = get_inv_matvec(M, symmetric=True, tol=tol) 

else: 

Minv = _aslinearoperator_with_dtype(Minv) 

Minv_matvec = Minv.matvec 

M_matvec = _aslinearoperator_with_dtype(M).matvec 

else: 

#sigma is not None: shift-invert mode 

if np.issubdtype(A.dtype, np.complexfloating): 

if OPpart is not None: 

raise ValueError("OPpart should not be specified " 

"with sigma=None or complex A") 

mode = 3 

elif OPpart is None or OPpart.lower() == 'r': 

mode = 3 

elif OPpart.lower() == 'i': 

if np.imag(sigma) == 0: 

raise ValueError("OPpart cannot be 'i' if sigma is real") 

mode = 4 

else: 

raise ValueError("OPpart must be one of ('r','i')") 

 

matvec = _aslinearoperator_with_dtype(A).matvec 

if Minv is not None: 

raise ValueError("Minv should not be specified when sigma is") 

if OPinv is None: 

Minv_matvec = get_OPinv_matvec(A, M, sigma, 

symmetric=False, tol=tol) 

else: 

OPinv = _aslinearoperator_with_dtype(OPinv) 

Minv_matvec = OPinv.matvec 

if M is None: 

M_matvec = None 

else: 

M_matvec = _aslinearoperator_with_dtype(M).matvec 

 

params = _UnsymmetricArpackParams(n, k, A.dtype.char, matvec, mode, 

M_matvec, Minv_matvec, sigma, 

ncv, v0, maxiter, which, tol) 

 

with _ARPACK_LOCK: 

while not params.converged: 

params.iterate() 

 

return params.extract(return_eigenvectors) 

 

 

def eigsh(A, k=6, M=None, sigma=None, which='LM', v0=None, 

ncv=None, maxiter=None, tol=0, return_eigenvectors=True, 

Minv=None, OPinv=None, mode='normal'): 

""" 

Find k eigenvalues and eigenvectors of the real symmetric square matrix 

or complex hermitian matrix A. 

 

Solves ``A * x[i] = w[i] * x[i]``, the standard eigenvalue problem for 

w[i] eigenvalues with corresponding eigenvectors x[i]. 

 

If M is specified, solves ``A * x[i] = w[i] * M * x[i]``, the 

generalized eigenvalue problem for w[i] eigenvalues 

with corresponding eigenvectors x[i] 

 

Parameters 

---------- 

A : An N x N matrix, array, sparse matrix, or LinearOperator representing 

the operation A * x, where A is a real symmetric matrix 

For buckling mode (see below) A must additionally be positive-definite 

k : int, optional 

The number of eigenvalues and eigenvectors desired. 

`k` must be smaller than N. It is not possible to compute all 

eigenvectors of a matrix. 

 

Returns 

------- 

w : array 

Array of k eigenvalues 

v : array 

An array representing the `k` eigenvectors. The column ``v[:, i]`` is 

the eigenvector corresponding to the eigenvalue ``w[i]``. 

 

Other Parameters 

---------------- 

M : An N x N matrix, array, sparse matrix, or linear operator representing 

the operation M * x for the generalized eigenvalue problem 

 

A * x = w * M * x. 

 

M must represent a real, symmetric matrix if A is real, and must 

represent a complex, hermitian matrix if A is complex. For best 

results, the data type of M should be the same as that of A. 

Additionally: 

 

If sigma is None, M is symmetric positive definite 

 

If sigma is specified, M is symmetric positive semi-definite 

 

In buckling mode, M is symmetric indefinite. 

 

If sigma is None, eigsh requires an operator to compute the solution 

of the linear equation ``M * x = b``. This is done internally via a 

(sparse) LU decomposition for an explicit matrix M, or via an 

iterative solver for a general linear operator. Alternatively, 

the user can supply the matrix or operator Minv, which gives 

``x = Minv * b = M^-1 * b``. 

sigma : real 

Find eigenvalues near sigma using shift-invert mode. This requires 

an operator to compute the solution of the linear system 

`[A - sigma * M] x = b`, where M is the identity matrix if 

unspecified. This is computed internally via a (sparse) LU 

decomposition for explicit matrices A & M, or via an iterative 

solver if either A or M is a general linear operator. 

Alternatively, the user can supply the matrix or operator OPinv, 

which gives ``x = OPinv * b = [A - sigma * M]^-1 * b``. 

Note that when sigma is specified, the keyword 'which' refers to 

the shifted eigenvalues ``w'[i]`` where: 

 

if mode == 'normal', ``w'[i] = 1 / (w[i] - sigma)``. 

 

if mode == 'cayley', ``w'[i] = (w[i] + sigma) / (w[i] - sigma)``. 

 

if mode == 'buckling', ``w'[i] = w[i] / (w[i] - sigma)``. 

 

(see further discussion in 'mode' below) 

v0 : ndarray, optional 

Starting vector for iteration. 

Default: random 

ncv : int, optional 

The number of Lanczos vectors generated ncv must be greater than k and 

smaller than n; it is recommended that ``ncv > 2*k``. 

Default: ``min(n, max(2*k + 1, 20))`` 

which : str ['LM' | 'SM' | 'LA' | 'SA' | 'BE'] 

If A is a complex hermitian matrix, 'BE' is invalid. 

Which `k` eigenvectors and eigenvalues to find: 

 

'LM' : Largest (in magnitude) eigenvalues 

 

'SM' : Smallest (in magnitude) eigenvalues 

 

'LA' : Largest (algebraic) eigenvalues 

 

'SA' : Smallest (algebraic) eigenvalues 

 

'BE' : Half (k/2) from each end of the spectrum 

 

When k is odd, return one more (k/2+1) from the high end. 

When sigma != None, 'which' refers to the shifted eigenvalues ``w'[i]`` 

(see discussion in 'sigma', above). ARPACK is generally better 

at finding large values than small values. If small eigenvalues are 

desired, consider using shift-invert mode for better performance. 

maxiter : int, optional 

Maximum number of Arnoldi update iterations allowed 

Default: ``n*10`` 

tol : float 

Relative accuracy for eigenvalues (stopping criterion). 

The default value of 0 implies machine precision. 

Minv : N x N matrix, array, sparse matrix, or LinearOperator 

See notes in M, above 

OPinv : N x N matrix, array, sparse matrix, or LinearOperator 

See notes in sigma, above. 

return_eigenvectors : bool 

Return eigenvectors (True) in addition to eigenvalues 

mode : string ['normal' | 'buckling' | 'cayley'] 

Specify strategy to use for shift-invert mode. This argument applies 

only for real-valued A and sigma != None. For shift-invert mode, 

ARPACK internally solves the eigenvalue problem 

``OP * x'[i] = w'[i] * B * x'[i]`` 

and transforms the resulting Ritz vectors x'[i] and Ritz values w'[i] 

into the desired eigenvectors and eigenvalues of the problem 

``A * x[i] = w[i] * M * x[i]``. 

The modes are as follows: 

 

'normal' : 

OP = [A - sigma * M]^-1 * M, 

B = M, 

w'[i] = 1 / (w[i] - sigma) 

 

'buckling' : 

OP = [A - sigma * M]^-1 * A, 

B = A, 

w'[i] = w[i] / (w[i] - sigma) 

 

'cayley' : 

OP = [A - sigma * M]^-1 * [A + sigma * M], 

B = M, 

w'[i] = (w[i] + sigma) / (w[i] - sigma) 

 

The choice of mode will affect which eigenvalues are selected by 

the keyword 'which', and can also impact the stability of 

convergence (see [2] for a discussion) 

 

Raises 

------ 

ArpackNoConvergence 

When the requested convergence is not obtained. 

 

The currently converged eigenvalues and eigenvectors can be found 

as ``eigenvalues`` and ``eigenvectors`` attributes of the exception 

object. 

 

See Also 

-------- 

eigs : eigenvalues and eigenvectors for a general (nonsymmetric) matrix A 

svds : singular value decomposition for a matrix A 

 

Notes 

----- 

This function is a wrapper to the ARPACK [1]_ SSEUPD and DSEUPD 

functions which use the Implicitly Restarted Lanczos Method to 

find the eigenvalues and eigenvectors [2]_. 

 

References 

---------- 

.. [1] ARPACK Software, http://www.caam.rice.edu/software/ARPACK/ 

.. [2] R. B. Lehoucq, D. C. Sorensen, and C. Yang, ARPACK USERS GUIDE: 

Solution of Large Scale Eigenvalue Problems by Implicitly Restarted 

Arnoldi Methods. SIAM, Philadelphia, PA, 1998. 

 

Examples 

-------- 

>>> import scipy.sparse as sparse 

>>> id = np.eye(13) 

>>> vals, vecs = sparse.linalg.eigsh(id, k=6) 

>>> vals 

array([ 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j]) 

>>> vecs.shape 

(13, 6) 

 

""" 

# complex hermitian matrices should be solved with eigs 

if np.issubdtype(A.dtype, np.complexfloating): 

if mode != 'normal': 

raise ValueError("mode=%s cannot be used with " 

"complex matrix A" % mode) 

if which == 'BE': 

raise ValueError("which='BE' cannot be used with complex matrix A") 

elif which == 'LA': 

which = 'LR' 

elif which == 'SA': 

which = 'SR' 

ret = eigs(A, k, M=M, sigma=sigma, which=which, v0=v0, 

ncv=ncv, maxiter=maxiter, tol=tol, 

return_eigenvectors=return_eigenvectors, Minv=Minv, 

OPinv=OPinv) 

 

if return_eigenvectors: 

return ret[0].real, ret[1] 

else: 

return ret.real 

 

if A.shape[0] != A.shape[1]: 

raise ValueError('expected square matrix (shape=%s)' % (A.shape,)) 

if M is not None: 

if M.shape != A.shape: 

raise ValueError('wrong M dimensions %s, should be %s' 

% (M.shape, A.shape)) 

if np.dtype(M.dtype).char.lower() != np.dtype(A.dtype).char.lower(): 

warnings.warn('M does not have the same type precision as A. ' 

'This may adversely affect ARPACK convergence') 

 

n = A.shape[0] 

 

if k <= 0: 

raise ValueError("k must be greater than 0.") 

 

if k >= n: 

warnings.warn("k >= N for N * N square matrix. " 

"Attempting to use scipy.linalg.eigh instead.", 

RuntimeWarning) 

 

if issparse(A): 

raise TypeError("Cannot use scipy.linalg.eigh for sparse A with " 

"k >= N. Use scipy.linalg.eigh(A.toarray()) or" 

" reduce k.") 

if isinstance(A, LinearOperator): 

raise TypeError("Cannot use scipy.linalg.eigh for LinearOperator " 

"A with k >= N.") 

if isinstance(M, LinearOperator): 

raise TypeError("Cannot use scipy.linalg.eigh for LinearOperator " 

"M with k >= N.") 

 

return eigh(A, b=M, eigvals_only=not return_eigenvectors) 

 

if sigma is None: 

A = _aslinearoperator_with_dtype(A) 

matvec = A.matvec 

 

if OPinv is not None: 

raise ValueError("OPinv should not be specified " 

"with sigma = None.") 

if M is None: 

#standard eigenvalue problem 

mode = 1 

M_matvec = None 

Minv_matvec = None 

if Minv is not None: 

raise ValueError("Minv should not be " 

"specified with M = None.") 

else: 

#general eigenvalue problem 

mode = 2 

if Minv is None: 

Minv_matvec = get_inv_matvec(M, symmetric=True, tol=tol) 

else: 

Minv = _aslinearoperator_with_dtype(Minv) 

Minv_matvec = Minv.matvec 

M_matvec = _aslinearoperator_with_dtype(M).matvec 

else: 

# sigma is not None: shift-invert mode 

if Minv is not None: 

raise ValueError("Minv should not be specified when sigma is") 

 

# normal mode 

if mode == 'normal': 

mode = 3 

matvec = None 

if OPinv is None: 

Minv_matvec = get_OPinv_matvec(A, M, sigma, 

symmetric=True, tol=tol) 

else: 

OPinv = _aslinearoperator_with_dtype(OPinv) 

Minv_matvec = OPinv.matvec 

if M is None: 

M_matvec = None 

else: 

M = _aslinearoperator_with_dtype(M) 

M_matvec = M.matvec 

 

# buckling mode 

elif mode == 'buckling': 

mode = 4 

if OPinv is None: 

Minv_matvec = get_OPinv_matvec(A, M, sigma, 

symmetric=True, tol=tol) 

else: 

Minv_matvec = _aslinearoperator_with_dtype(OPinv).matvec 

matvec = _aslinearoperator_with_dtype(A).matvec 

M_matvec = None 

 

# cayley-transform mode 

elif mode == 'cayley': 

mode = 5 

matvec = _aslinearoperator_with_dtype(A).matvec 

if OPinv is None: 

Minv_matvec = get_OPinv_matvec(A, M, sigma, 

symmetric=True, tol=tol) 

else: 

Minv_matvec = _aslinearoperator_with_dtype(OPinv).matvec 

if M is None: 

M_matvec = None 

else: 

M_matvec = _aslinearoperator_with_dtype(M).matvec 

 

# unrecognized mode 

else: 

raise ValueError("unrecognized mode '%s'" % mode) 

 

params = _SymmetricArpackParams(n, k, A.dtype.char, matvec, mode, 

M_matvec, Minv_matvec, sigma, 

ncv, v0, maxiter, which, tol) 

 

with _ARPACK_LOCK: 

while not params.converged: 

params.iterate() 

 

return params.extract(return_eigenvectors) 

 

 

def _augmented_orthonormal_cols(x, k): 

# extract the shape of the x array 

n, m = x.shape 

# create the expanded array and copy x into it 

y = np.empty((n, m+k), dtype=x.dtype) 

y[:, :m] = x 

# do some modified gram schmidt to add k random orthonormal vectors 

for i in range(k): 

# sample a random initial vector 

v = np.random.randn(n) 

if np.iscomplexobj(x): 

v = v + 1j*np.random.randn(n) 

# subtract projections onto the existing unit length vectors 

for j in range(m+i): 

u = y[:, j] 

v -= (np.dot(v, u.conj()) / np.dot(u, u.conj())) * u 

# normalize v 

v /= np.sqrt(np.dot(v, v.conj())) 

# add v into the output array 

y[:, m+i] = v 

# return the expanded array 

return y 

 

 

def _augmented_orthonormal_rows(x, k): 

return _augmented_orthonormal_cols(x.T, k).T 

 

 

def _herm(x): 

return x.T.conj() 

 

 

def svds(A, k=6, ncv=None, tol=0, which='LM', v0=None, 

maxiter=None, return_singular_vectors=True): 

"""Compute the largest k singular values/vectors for a sparse matrix. 

 

Parameters 

---------- 

A : {sparse matrix, LinearOperator} 

Array to compute the SVD on, of shape (M, N) 

k : int, optional 

Number of singular values and vectors to compute. 

Must be 1 <= k < min(A.shape). 

ncv : int, optional 

The number of Lanczos vectors generated 

ncv must be greater than k+1 and smaller than n; 

it is recommended that ncv > 2*k 

Default: ``min(n, max(2*k + 1, 20))`` 

tol : float, optional 

Tolerance for singular values. Zero (default) means machine precision. 

which : str, ['LM' | 'SM'], optional 

Which `k` singular values to find: 

 

- 'LM' : largest singular values 

- 'SM' : smallest singular values 

 

.. versionadded:: 0.12.0 

v0 : ndarray, optional 

Starting vector for iteration, of length min(A.shape). Should be an 

(approximate) left singular vector if N > M and a right singular 

vector otherwise. 

Default: random 

 

.. versionadded:: 0.12.0 

maxiter : int, optional 

Maximum number of iterations. 

 

.. versionadded:: 0.12.0 

return_singular_vectors : bool or str, optional 

- True: return singular vectors (True) in addition to singular values. 

 

.. versionadded:: 0.12.0 

 

- "u": only return the u matrix, without computing vh (if N > M). 

- "vh": only return the vh matrix, without computing u (if N <= M). 

 

.. versionadded:: 0.16.0 

 

Returns 

------- 

u : ndarray, shape=(M, k) 

Unitary matrix having left singular vectors as columns. 

If `return_singular_vectors` is "vh", this variable is not computed, 

and None is returned instead. 

s : ndarray, shape=(k,) 

The singular values. 

vt : ndarray, shape=(k, N) 

Unitary matrix having right singular vectors as rows. 

If `return_singular_vectors` is "u", this variable is not computed, 

and None is returned instead. 

 

 

Notes 

----- 

This is a naive implementation using ARPACK as an eigensolver 

on A.H * A or A * A.H, depending on which one is more efficient. 

 

Examples 

-------- 

>>> from scipy.sparse import csc_matrix 

>>> from scipy.sparse.linalg import svds, eigs 

>>> A = csc_matrix([[1, 0, 0], [5, 0, 2], [0, -1, 0], [0, 0, 3]], dtype=float) 

>>> u, s, vt = svds(A, k=2) 

>>> s 

array([ 2.75193379, 5.6059665 ]) 

>>> np.sqrt(eigs(A.dot(A.T), k=2)[0]).real 

array([ 5.6059665 , 2.75193379]) 

""" 

if not (isinstance(A, LinearOperator) or isspmatrix(A)): 

A = np.asarray(A) 

 

n, m = A.shape 

 

if k <= 0 or k >= min(n, m): 

raise ValueError("k must be between 1 and min(A.shape), k=%d" % k) 

 

if isinstance(A, LinearOperator): 

if n > m: 

X_dot = A.matvec 

X_matmat = A.matmat 

XH_dot = A.rmatvec 

else: 

X_dot = A.rmatvec 

XH_dot = A.matvec 

 

dtype = getattr(A, 'dtype', None) 

if dtype is None: 

dtype = A.dot(np.zeros([m,1])).dtype 

 

# A^H * V; works around lack of LinearOperator.adjoint. 

# XXX This can be slow! 

def X_matmat(V): 

out = np.empty((V.shape[1], m), dtype=dtype) 

for i, col in enumerate(V.T): 

out[i, :] = A.rmatvec(col.reshape(-1, 1)).T 

return out.T 

 

else: 

if n > m: 

X_dot = X_matmat = A.dot 

XH_dot = _herm(A).dot 

else: 

XH_dot = A.dot 

X_dot = X_matmat = _herm(A).dot 

 

def matvec_XH_X(x): 

return XH_dot(X_dot(x)) 

 

XH_X = LinearOperator(matvec=matvec_XH_X, dtype=A.dtype, 

shape=(min(A.shape), min(A.shape))) 

 

# Get a low rank approximation of the implicitly defined gramian matrix. 

# This is not a stable way to approach the problem. 

eigvals, eigvec = eigsh(XH_X, k=k, tol=tol ** 2, maxiter=maxiter, 

ncv=ncv, which=which, v0=v0) 

 

# In 'LM' mode try to be clever about small eigenvalues. 

# Otherwise in 'SM' mode do not try to be clever. 

if which == 'LM': 

 

# Gramian matrices have real non-negative eigenvalues. 

eigvals = np.maximum(eigvals.real, 0) 

 

# Use the sophisticated detection of small eigenvalues from pinvh. 

t = eigvec.dtype.char.lower() 

factor = {'f': 1E3, 'd': 1E6} 

cond = factor[t] * np.finfo(t).eps 

cutoff = cond * np.max(eigvals) 

 

# Get a mask indicating which eigenpairs are not degenerately tiny, 

# and create the re-ordered array of thresholded singular values. 

above_cutoff = (eigvals > cutoff) 

nlarge = above_cutoff.sum() 

nsmall = k - nlarge 

slarge = np.sqrt(eigvals[above_cutoff]) 

s = np.zeros_like(eigvals) 

s[:nlarge] = slarge 

if not return_singular_vectors: 

return s 

 

if n > m: 

vlarge = eigvec[:, above_cutoff] 

ularge = X_matmat(vlarge) / slarge if return_singular_vectors != 'vh' else None 

vhlarge = _herm(vlarge) 

else: 

ularge = eigvec[:, above_cutoff] 

vhlarge = _herm(X_matmat(ularge) / slarge) if return_singular_vectors != 'u' else None 

 

u = _augmented_orthonormal_cols(ularge, nsmall) if ularge is not None else None 

vh = _augmented_orthonormal_rows(vhlarge, nsmall) if vhlarge is not None else None 

 

elif which == 'SM': 

 

s = np.sqrt(eigvals) 

if not return_singular_vectors: 

return s 

 

if n > m: 

v = eigvec 

u = X_matmat(v) / s if return_singular_vectors != 'vh' else None 

vh = _herm(v) 

else: 

u = eigvec 

vh = _herm(X_matmat(u) / s) if return_singular_vectors != 'u' else None 

 

else: 

 

raise ValueError("which must be either 'LM' or 'SM'.") 

 

return u, s, vh