mirror of
https://gitlab.com/libeigen/eigen.git
synced 2026-01-18 17:31:19 +01:00
272 lines
11 KiB
Python
272 lines
11 KiB
Python
# -*- coding: utf-8 -*-
|
|
# This file is part of Eigen, a lightweight C++ template library
|
|
# for linear algebra.
|
|
#
|
|
# Copyright (C) 2021 Huang, Zhaoquan <zhaoquan2008@hotmail.com>
|
|
# Copyright (C) 2025 Prince Mathew, Joseph <josephrpincemathew@outlook.com
|
|
#
|
|
# This Source Code Form is subject to the terms of the Mozilla Public
|
|
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
|
|
# Pretty printers for Eigen::Matrix to use with LLDB debugger
|
|
#
|
|
# Usage:
|
|
# 1. Add the following line (change it according to the path to this file)
|
|
# to the file ~/.lldbinit (create one if it doesn't exist):
|
|
# `command script import /path/to/eigenlldb.py`
|
|
# 2. Inspect the variables in LLDB command line
|
|
# `frame variable`
|
|
|
|
import lldb
|
|
from typing import List
|
|
import bisect
|
|
|
|
|
|
def __lldb_init_module(debugger, internal_dict):
|
|
debugger.HandleCommand(
|
|
"type synthetic add -x ^Eigen::Matrix<.*> --python-class eigenlldb.EigenMatrixChildProvider")
|
|
debugger.HandleCommand(
|
|
"type summary add -e -x ^Eigen::Matrix<.*> --python-function eigenlldb.eigen_matrix_summary_provider")
|
|
debugger.HandleCommand(
|
|
"type synthetic add -x ^Eigen::SparseMatrix<.*> --python-class eigenlldb.EigenSparseMatrixChildProvider")
|
|
|
|
class EigenMatrixProvider:
|
|
def __init__(self, valobj: lldb.SBValue, internal_dict):
|
|
|
|
if valobj.IsSynthetic():
|
|
self._valobj: lldb.SBValue = valobj.GetNonSyntheticValue()
|
|
else:
|
|
self._valobj: lldb.SBValue = valobj
|
|
|
|
_valtype = self._valobj.GetType().GetCanonicalType()
|
|
|
|
scalar_type = _valtype.GetTemplateArgumentType(0)
|
|
if not scalar_type.IsValid():
|
|
# In the case that scalar_type is invalid on LLDB 9.0 on Windows with CLion
|
|
storage = self._valobj.GetChildMemberWithName("m_storage")
|
|
data = storage.GetChildMemberWithName("m_data")
|
|
data_type = data.GetType()
|
|
if data_type.IsPointerType():
|
|
scalar_type = data.GetType().GetPointeeType()
|
|
else:
|
|
scalar_type = data.GetChildMemberWithName("array").GetType().GetArrayElementType()
|
|
self._scalar_type: lldb.SBType = scalar_type
|
|
self._scalar_size: int = self._scalar_type.GetByteSize()
|
|
|
|
name = _valtype.GetName()
|
|
template_begin = name.find("<")
|
|
template_end = name.rfind(">")
|
|
template_args = name[(template_begin + 1):template_end].split(",")
|
|
|
|
template_args = [arg.strip() for arg in template_args]
|
|
|
|
# Provide default values for missing template arguments
|
|
# Eigen::Matrix<Scalar, Rows, Cols, Options, MaxRows, MaxCols>
|
|
try:
|
|
self._rows_compile_time: int = int(template_args[1]) if len(template_args) > 1 else -1
|
|
except (ValueError, IndexError):
|
|
self._rows_compile_time: int = -1
|
|
|
|
try:
|
|
self._cols_compile_time: int = int(template_args[2]) if len(template_args) > 2 else -1
|
|
except (ValueError, IndexError):
|
|
self._cols_compile_time: int = -1
|
|
|
|
try:
|
|
self._options: int = int(template_args[3]) if len(template_args) > 3 else 0
|
|
self._row_major: bool = (self._options & 1) != 0
|
|
except (ValueError, IndexError):
|
|
self._options = 0
|
|
self._row_major: bool = False
|
|
|
|
try:
|
|
max_rows = int(template_args[4]) if len(template_args) > 4 else self._rows_compile_time
|
|
except (ValueError, IndexError):
|
|
max_rows = self._rows_compile_time
|
|
|
|
try:
|
|
max_cols = int(template_args[5]) if len(template_args) > 5 else self._cols_compile_time
|
|
except (ValueError, IndexError):
|
|
max_cols = self._cols_compile_time
|
|
|
|
self._fixed_storage: bool = (max_rows != -1 and max_cols != -1)
|
|
|
|
def _cols(self):
|
|
if self._cols_compile_time == -1:
|
|
storage = self._valobj.GetChildMemberWithName("m_storage")
|
|
cols = storage.GetChildMemberWithName("m_cols").GetValueAsUnsigned()
|
|
return cols
|
|
else:
|
|
return self._cols_compile_time
|
|
|
|
def _rows(self):
|
|
if self._rows_compile_time == -1:
|
|
storage = self._valobj.GetChildMemberWithName("m_storage")
|
|
rows = storage.GetChildMemberWithName("m_rows").GetValueAsUnsigned()
|
|
return rows
|
|
else:
|
|
return self._rows_compile_time
|
|
def get_summary(self):
|
|
if self._rows() == 0 or self._cols() == 0:
|
|
return f"size=[0x0, order={'row_major' if self._row_major else 'column_major'}]"
|
|
else:
|
|
return f"size=[{self._rows()}x{self._cols()}, order={'row_major' if self._row_major else 'column_major'}] "
|
|
|
|
|
|
def eigen_matrix_summary_provider(valobj, dict):
|
|
prov = EigenMatrixProvider(valobj, dict)
|
|
return prov.get_summary()
|
|
|
|
class EigenMatrixChildProvider(EigenMatrixProvider):
|
|
|
|
def num_children(self):
|
|
return self._cols() * self._rows()
|
|
|
|
def get_child_index(self, name):
|
|
pass
|
|
|
|
def get_child_at_index(self, index):
|
|
storage = self._valobj.GetChildMemberWithName("m_storage")
|
|
data = storage.GetChildMemberWithName("m_data")
|
|
offset = self._scalar_size * index
|
|
|
|
if self._row_major:
|
|
row = index // self._cols()
|
|
col = index % self._cols()
|
|
else:
|
|
row = index % self._rows()
|
|
col = index // self._rows()
|
|
if self._fixed_storage:
|
|
data = data.GetChildMemberWithName("array")
|
|
if self._cols() == 1:
|
|
name = '[{}]'.format(row)
|
|
elif self._rows() == 1:
|
|
name = '[{}]'.format(col)
|
|
else:
|
|
name = '[{},{}]'.format(row, col)
|
|
return data.CreateChildAtOffset(
|
|
name, offset, self._scalar_type
|
|
)
|
|
|
|
class EigenSparseMatrixChildProvider:
|
|
_valobj: lldb.SBValue
|
|
_scalar_type: lldb.SBType
|
|
_scalar_size: int
|
|
_index_type: lldb.SBType
|
|
_index_size: int
|
|
_row_major: bool
|
|
|
|
_outer_size: int
|
|
_nnz: int
|
|
_values: lldb.SBValue
|
|
_inner_indices: lldb.SBValue
|
|
_outer_starts: lldb.SBValue
|
|
_inner_nnzs: lldb.SBValue
|
|
_compressed: bool
|
|
|
|
# Index of the first synthetic child under each outer index
|
|
_child_indices: List[int]
|
|
|
|
def __init__(self, valobj, internal_dict):
|
|
self._valobj = valobj
|
|
valtype = valobj.GetType().GetCanonicalType()
|
|
scalar_type = valtype.GetTemplateArgumentType(0)
|
|
if not scalar_type.IsValid():
|
|
# In the case that scalar_type is invalid on LLDB 9.0 on Windows with CLion
|
|
data = valobj.GetChildMemberWithName("m_data")
|
|
values = data.GetChildMemberWithName("m_values")
|
|
scalar_type = values.GetType().GetPointeeType()
|
|
self._scalar_type = scalar_type
|
|
self._scalar_size = scalar_type.GetByteSize()
|
|
|
|
index_type = valtype.GetTemplateArgumentType(2)
|
|
if not index_type.IsValid():
|
|
# In the case that scalar_type is invalid on LLDB 9.0 on Windows with CLion
|
|
outer_starts = valobj.GetChildMemberWithName("m_outerIndex")
|
|
index_type = outer_starts.GetType().GetPointeeType()
|
|
self._index_type = index_type
|
|
self._index_size = index_type.GetByteSize()
|
|
|
|
name = valtype.GetName()
|
|
template_begin = name.find("<")
|
|
template_end = name.find(">")
|
|
template_args = name[(template_begin + 1):template_end].split(",")
|
|
self._row_major = (int(template_args[1]) & 1) != 0
|
|
|
|
def num_children(self):
|
|
return self._nnz + 2
|
|
|
|
def get_child_index(self, name):
|
|
pass
|
|
|
|
def get_child_at_index(self, index):
|
|
if index == 0:
|
|
name = "rows" if self._row_major else "cols"
|
|
return self._valobj.GetChildMemberWithName("m_outerSize") \
|
|
.CreateChildAtOffset(name, 0, self._index_type)
|
|
elif index == 1:
|
|
name = "cols" if self._row_major else "rows"
|
|
return self._valobj.GetChildMemberWithName("m_innerSize") \
|
|
.CreateChildAtOffset(name, 0, self._index_type)
|
|
else:
|
|
index = index - 2
|
|
outer_index = bisect.bisect_right(self._child_indices, index) - 1
|
|
total_nnzs = self._child_indices[outer_index]
|
|
if self._compressed:
|
|
item_index = index
|
|
inner_index = self._inner_indices \
|
|
.CreateChildAtOffset("", item_index * self._index_size, self._index_type) \
|
|
.GetValueAsUnsigned()
|
|
return self._values \
|
|
.CreateChildAtOffset(self._child_name(outer_index, inner_index),
|
|
item_index * self._scalar_size,
|
|
self._scalar_type)
|
|
else:
|
|
index_begin = self._outer_starts \
|
|
.CreateChildAtOffset("", outer_index * self._index_size, self._index_type) \
|
|
.GetValueAsUnsigned()
|
|
item_index = index - total_nnzs + index_begin
|
|
inner_index = self._inner_indices \
|
|
.CreateChildAtOffset("", item_index * self._index_size, self._index_type) \
|
|
.GetValueAsUnsigned()
|
|
return self._values \
|
|
.CreateChildAtOffset(self._child_name(outer_index, inner_index),
|
|
item_index * self._scalar_size,
|
|
self._scalar_type)
|
|
|
|
def update(self):
|
|
valobj = self._valobj
|
|
self._outer_size = valobj.GetChildMemberWithName("m_outerSize").GetValueAsUnsigned()
|
|
data = valobj.GetChildMemberWithName("m_data")
|
|
self._values = data.GetChildMemberWithName("m_values")
|
|
self._inner_indices = data.GetChildMemberWithName("m_indices")
|
|
self._outer_starts = valobj.GetChildMemberWithName("m_outerIndex")
|
|
self._inner_nnzs = valobj.GetChildMemberWithName("m_innerNonZeros")
|
|
|
|
self._compressed = self._inner_nnzs.GetValueAsUnsigned() == 0
|
|
|
|
total_nnzs = 0
|
|
child_indices = [0]
|
|
for outer_index in range(self._outer_size):
|
|
if self._compressed:
|
|
index_end = self._outer_starts \
|
|
.CreateChildAtOffset("", (outer_index + 1) * self._index_size, self._index_type) \
|
|
.GetValueAsUnsigned()
|
|
total_nnzs = index_end
|
|
child_indices.append(total_nnzs)
|
|
else:
|
|
nnzs = self._inner_nnzs \
|
|
.CreateChildAtOffset("", outer_index * self._index_size, self._index_type) \
|
|
.GetValueAsUnsigned()
|
|
total_nnzs = total_nnzs + nnzs
|
|
child_indices.append(total_nnzs)
|
|
self._child_indices = child_indices
|
|
self._nnz = total_nnzs
|
|
|
|
def _child_name(self, outer_index, inner_index):
|
|
if self._row_major:
|
|
return "[{0},{1}]".format(outer_index, inner_index)
|
|
else:
|
|
return "[{1},{0}]".format(outer_index, inner_index)
|