remove np_utils Matrix and Vector functions

Not needed anymore.
release/4.3a0
Duy-Nguyen Ta 2017-03-15 13:54:00 -04:00
parent 6bf7ea23cf
commit 685b0cb62f
11 changed files with 90 additions and 89 deletions

View File

@ -2,7 +2,7 @@ This is the Cython/Python wrapper around the GTSAM C++ library.
INSTALL INSTALL
======= =======
- This wrapper needs Cython(>=0.25), numpy and eigency, which can be installed - This wrapper needs Cython(>=0.25), numpy and eigency, which can be installed
as follows: as follows:
```bash ```bash
@ -35,21 +35,27 @@ See the tests for examples.
## Some important notes: ## Some important notes:
- Vector/Matrix: Due to a design choice of eigency, numpy.array matrices with the default order='A' - Vector/Matrix:
will always be transposed in C++ no matter how you transpose it in Python. Use order='F', or use + GTSAM expects double-precision floating point vectors and matrices.
two functions Vector and Matrix in cython/gtsam/utils/np_utils.py for your conveniences. These two functions Hence, you should pass numpy matrices with dtype=float, or 'float64'.
also help to avoid a common but very subtle bug of using integers when creating numpy arrays, + Also, GTSAM expects *column-major* matrices, unlike the default storage
e.g. np.array([1,2,3]). These can't be an input for gtsam functions as they only accept floating-point arrays. scheme in numpy. Hence, you should pass column-major matrices to gtsam using
For more details, see: https://github.com/wouterboomsma/eigency#storage-layout---why-arrays-are-sometimes-transposed the flag order='F'. And you always get column-major matrices back.
For more details, see: https://github.com/wouterboomsma/eigency#storage-layout---why-arrays-are-sometimes-transposed
+ Passing row-major matrices of different dtype, e.g. 'int', will also work
as the wrapper converts them to column-major and dtype float for you,
using numpy.array.astype(float, order='F', copy=False).
However, this will result a copy if your matrix is not in the expected type
and storage order.
- Inner namespace: Classes in inner namespace will be prefixed by <innerNamespace>_ in Python. - Inner namespace: Classes in inner namespace will be prefixed by <innerNamespace>_ in Python.
Examples: noiseModel_Gaussian, noiseModel_mEstimator_Tukey Examples: noiseModel_Gaussian, noiseModel_mEstimator_Tukey
- Casting from a base class to a derive class must be done explicitly. - Casting from a base class to a derive class must be done explicitly.
Examples: Examples:
```Python ```Python
noiseBase = factor.get_noiseModel() noiseBase = factor.get_noiseModel()
noiseGaussian = dynamic_cast_noiseModel_Gaussian_noiseModel_Base(noiseBase) noiseGaussian = dynamic_cast_noiseModel_Gaussian_noiseModel_Base(noiseBase)
``` ```
WRAPPING YOUR OWN PROJECT THAT USES GTSAM WRAPPING YOUR OWN PROJECT THAT USES GTSAM
@ -102,7 +108,7 @@ KNOWN ISSUES
- size-related issue: can only wrap up to a certain number of classes: up to mEstimator! - size-related issue: can only wrap up to a certain number of classes: up to mEstimator!
- Guess: 64 vs 32b? disutils Compiler flags? - Guess: 64 vs 32b? disutils Compiler flags?
- Bug with Cython 0.24: instantiated factor classes return FastVector<size_t> for keys(), which can't be casted to FastVector<Key> - Bug with Cython 0.24: instantiated factor classes return FastVector<size_t> for keys(), which can't be casted to FastVector<Key>
- Upgrading to 0.25 solves the problem - Upgrading to 0.25 solves the problem
- Need default constructor and default copy constructor for almost every classes... :( - Need default constructor and default copy constructor for almost every classes... :(
- support these constructors by default and declare "delete" for special classes? - support these constructors by default and declare "delete" for special classes?
@ -110,12 +116,20 @@ KNOWN ISSUES
TODO TODO
===== =====
☐ Unify cython/gtsam.h and the original gtsam.h ☐ Unify cython/gtsam.h and the original gtsam.h
✔ 06-03-17: manage to remove the requirements for default and copy constructors
- 25-11-16: - 25-11-16:
Try to unify but failed. Main reasons are: Key/size_t, std containers, KeyVector/KeyList/KeySet. Try to unify but failed. Main reasons are: Key/size_t, std containers, KeyVector/KeyList/KeySet.
Matlab doesn't need to know about Key, but I can't make Cython to ignore Key as it couldn't cast KeyVector, i.e. FastVector<Key>, Matlab doesn't need to know about Key, but I can't make Cython to ignore Key as it couldn't cast KeyVector, i.e. FastVector<Key>,
to FastVector<size_t>. to FastVector<size_t>.
☐ Unit tests for cython wrappers
☐ Fix Python tests: don't use " import <package> * ": Bad style!!!
☐ Marginal and JointMarginal: revert changes
- add doc for generate
- matlab 6 arguments?
Completed/Cancelled: Completed/Cancelled:
✔ Convert input numpy Matrix/Vector to float dtype and storage order 'F' automatically, cannot crash! @done (15-03-17 13:00)
✔ Remove requirements.txt - Frank: don't bother with only 2 packages and a special case for eigency! @done (08-03-17 10:30)
✔ CMake install script @done (25-11-16 02:30) ✔ CMake install script @done (25-11-16 02:30)
✘ [REFACTOR] better name for uninstantiateClass: very vague!! @cancelled (25-11-16 02:30) -- lazy ✘ [REFACTOR] better name for uninstantiateClass: very vague!! @cancelled (25-11-16 02:30) -- lazy
✘ forward declaration? @cancelled (23-11-16 13:00) - nothing to do, seem to work? ✘ forward declaration? @cancelled (23-11-16 13:00) - nothing to do, seem to work?
@ -150,7 +164,7 @@ Completed/Cancelled:
- what's the purpose of "virtual" ?? - what's the purpose of "virtual" ??
Installation: Installation:
☐ Prerequisite: ☐ Prerequisite:
- Users create venv and pip install requirements before compiling - Users create venv and pip install requirements before compiling
- Wrap cython script in gtsam/cython folder - Wrap cython script in gtsam/cython folder
☐ Install built module into venv? ☐ Install built module into venv?

View File

@ -3,9 +3,9 @@ This file contains small experiments to test the wrapper with gtsam_short,
not real unittests. Its name convention is different from other tests so it not real unittests. Its name convention is different from other tests so it
won't be discovered. won't be discovered.
""" """
from gtsam.gtsam import * from gtsam import *
import numpy as np import numpy as np
from gtsam.utils import Vector, Matrix from utils import Vector, Matrix
r = Rot3() r = Rot3()
print(r) print(r)

View File

@ -2,36 +2,39 @@ import unittest
from gtsam import * from gtsam import *
from math import * from math import *
import numpy as np import numpy as np
from gtsam.utils import Matrix, Vector
class TestJacobianFactor(unittest.TestCase): class TestJacobianFactor(unittest.TestCase):
def test_eliminate(self): def test_eliminate(self):
Ax2 = Matrix( # Recommended way to specify a matrix (see cython/README)
[-5., 0.], Ax2 = np.array(
[[-5., 0.],
[+0., -5.], [+0., -5.],
[10., 0.], [10., 0.],
[+0., 10.]) [+0., 10.]], order='F')
Al1 = Matrix( # This is good too
[5., 0.], Al1 = np.array(
[0., 5.], [[5, 0],
[0., 0.], [0, 5],
[0., 0.]) [0, 0],
[0, 0]], dtype=float, order = 'F')
Ax1 = Matrix( # Not recommended for performance reasons, but should still work
[0.00, 0.], # f4 # as the wrapper should convert it to the correct type and storage order
[0.00, 0.], # f4 Ax1 = np.array(
[-10., 0.], # f2 [[0, 0], # f4
[0.00, -10.]) # f2 [0, 0], # f4
[-10, 0], # f2
[0, -10]]) # f2
x2 = 1 x2 = 1
l1 = 2 l1 = 2
x1 = 3 x1 = 3
# the RHS # the RHS
b2 = Vector(-1., 1.5, 2., -1.) b2 = np.array([-1., 1.5, 2., -1.])
sigmas = Vector(1., 1., 1., 1.) sigmas = np.array([1., 1., 1., 1.])
model4 = noiseModel_Diagonal.Sigmas(sigmas) model4 = noiseModel_Diagonal.Sigmas(sigmas)
combined = JacobianFactor(x2, Ax2, l1, Al1, x1, Ax1, b2, model4) combined = JacobianFactor(x2, Ax2, l1, Al1, x1, Ax1, b2, model4)
@ -42,29 +45,29 @@ class TestJacobianFactor(unittest.TestCase):
actualCG, lf = combined.eliminate(ord) actualCG, lf = combined.eliminate(ord)
# create expected Conditional Gaussian # create expected Conditional Gaussian
R11 = Matrix([11.1803, 0.00], R11 = np.array([[11.1803, 0.00],
[0.00, 11.1803]) [0.00, 11.1803]])
S12 = Matrix([-2.23607, 0.00], S12 = np.array([[-2.23607, 0.00],
[+0.00, -2.23607]) [+0.00, -2.23607]])
S13 = Matrix([-8.94427, 0.00], S13 = np.array([[-8.94427, 0.00],
[+0.00, -8.94427]) [+0.00, -8.94427]])
d = Vector(2.23607, -1.56525) d = np.array([2.23607, -1.56525])
expectedCG = GaussianConditional( expectedCG = GaussianConditional(
x2, d, R11, l1, S12, x1, S13, noiseModel_Unit.Create(2)) x2, d, R11, l1, S12, x1, S13, noiseModel_Unit.Create(2))
# check if the result matches # check if the result matches
self.assertTrue(actualCG.equals(expectedCG, 1e-4)) self.assertTrue(actualCG.equals(expectedCG, 1e-4))
# the expected linear factor # the expected linear factor
Bl1 = Matrix([4.47214, 0.00], Bl1 = np.array([[4.47214, 0.00],
[0.00, 4.47214]) [0.00, 4.47214]])
Bx1 = Matrix( Bx1 = np.array(
# x1 # x1
[-4.47214, 0.00], [[-4.47214, 0.00],
[+0.00, -4.47214]) [+0.00, -4.47214]])
# the RHS # the RHS
b1 = Vector(0.0, 0.894427) b1 = np.array([0.0, 0.894427])
model2 = noiseModel_Diagonal.Sigmas(np.array([1., 1.])) model2 = noiseModel_Diagonal.Sigmas(np.array([1., 1.]))
expectedLF = JacobianFactor(l1, Bl1, x1, Bx1, b1, model2) expectedLF = JacobianFactor(l1, Bl1, x1, Bx1, b1, model2)

View File

@ -2,7 +2,6 @@ import unittest
from gtsam import * from gtsam import *
from math import * from math import *
import numpy as np import numpy as np
from gtsam.utils import Vector, Matrix
import gtsam.utils.visual_data_generator as generator import gtsam.utils.visual_data_generator as generator
@ -17,7 +16,7 @@ class TestSFMExample(unittest.TestCase):
measurementNoiseSigma = 1.0 measurementNoiseSigma = 1.0
pointNoiseSigma = 0.1 pointNoiseSigma = 0.1
poseNoiseSigmas = Vector([0.001, 0.001, 0.001, 0.1, 0.1, 0.1]) poseNoiseSigmas = np.array([0.001, 0.001, 0.001, 0.1, 0.1, 0.1])
graph = NonlinearFactorGraph() graph = NonlinearFactorGraph()

View File

@ -2,7 +2,6 @@ import unittest
from gtsam import * from gtsam import *
from math import * from math import *
import numpy as np import numpy as np
from gtsam.utils import Vector, Matrix
class TestStereoVOExample(unittest.TestCase): class TestStereoVOExample(unittest.TestCase):
@ -32,7 +31,7 @@ class TestStereoVOExample(unittest.TestCase):
## Create realistic calibration and measurement noise model ## Create realistic calibration and measurement noise model
# format: fx fy skew cx cy baseline # format: fx fy skew cx cy baseline
K = Cal3_S2Stereo(1000, 1000, 0, 320, 240, 0.2) K = Cal3_S2Stereo(1000, 1000, 0, 320, 240, 0.2)
stereo_model = noiseModel_Diagonal.Sigmas(Vector([1.0, 1.0, 1.0])) stereo_model = noiseModel_Diagonal.Sigmas(np.array([1.0, 1.0, 1.0]))
## Add measurements ## Add measurements
# pose 1 # pose 1

View File

@ -22,11 +22,25 @@ class TestValues(unittest.TestCase):
values.insert(9, E) values.insert(9, E)
values.insert(10, imuBias_ConstantBias()) values.insert(10, imuBias_ConstantBias())
# special cases for Vector and Matrix: # Special cases for Vectors and Matrices
vec = np.array([1., 2., 3.]) # Note that gtsam's Eigen Vectors and Matrices requires double-precision
# floating point numbers in column-major (Fortran style) storage order,
# whereas by default, numpy.array is in row-major order and the type is
# in whatever the number input type is, e.g. np.array([1,2,3])
# will have 'int' type.
#
# The wrapper will automatically fix the type and storage order for you,
# but for performace reasons, it's recommended to specify the correct
# type and storage order.
vec = np.array([1., 2., 3.]) # for vectors, the order is not important, but dtype still is
values.insert(11, vec) values.insert(11, vec)
mat = np.array([[1., 2.], [3., 4.]], order='F') mat = np.array([[1., 2.], [3., 4.]], order='F')
values.insert(12, mat) values.insert(12, mat)
# Test with dtype int and the default order='C'
# This still works as the wrapper converts to the correct type and order for you
# but is nornally not recommended!
mat2 = np.array([[1,2,],[3,5]])
values.insert(13, mat2)
self.assertTrue(values.atPoint2(0).equals(Point2(), tol)) self.assertTrue(values.atPoint2(0).equals(Point2(), tol))
self.assertTrue(values.atPoint3(1).equals(Point3(), tol)) self.assertTrue(values.atPoint3(1).equals(Point3(), tol))
@ -46,6 +60,8 @@ class TestValues(unittest.TestCase):
self.assertTrue(np.allclose(vec, actualVector, tol)) self.assertTrue(np.allclose(vec, actualVector, tol))
actualMatrix = values.atMatrix(12) actualMatrix = values.atMatrix(12)
self.assertTrue(np.allclose(mat, actualMatrix, tol)) self.assertTrue(np.allclose(mat, actualMatrix, tol))
actualMatrix2 = values.atMatrix(13)
self.assertTrue(np.allclose(mat2, actualMatrix2, tol))
if __name__ == "__main__": if __name__ == "__main__":
unittest.main() unittest.main()

View File

@ -2,7 +2,6 @@ import unittest
from gtsam import * from gtsam import *
from math import * from math import *
import numpy as np import numpy as np
from gtsam.utils import Vector, Matrix
import gtsam.utils.visual_data_generator as generator import gtsam.utils.visual_data_generator as generator
import gtsam.utils.visual_isam as visual_isam import gtsam.utils.visual_isam as visual_isam

View File

@ -1 +0,0 @@
from .np_utils import *

View File

@ -1,6 +1,6 @@
from gtsam import * from gtsam import *
from math import * from math import *
from np_utils import * import numpy as np
def circlePose3(numPoses = 8, radius = 1.0, symbolChar = 0): def circlePose3(numPoses = 8, radius = 1.0, symbolChar = 0):
""" """
@ -23,7 +23,7 @@ def circlePose3(numPoses = 8, radius = 1.0, symbolChar = 0):
values = gtsam.Values() values = gtsam.Values()
theta = 0.0 theta = 0.0
dtheta = 2*pi/numPoses dtheta = 2*pi/numPoses
gRo = gtsam.Rot3(Matrix([0., 1., 0.], [1., 0., 0.], [0., 0., -1.])) gRo = gtsam.Rot3(np.array([[0., 1., 0.], [1., 0., 0.], [0., 0., -1.]], order='F'))
for i in range(numPoses): for i in range(numPoses):
key = gtsam.symbol(symbolChar, i) key = gtsam.symbol(symbolChar, i)
gti = gtsam.Point3(radius*cos(theta), radius*sin(theta), 0) gti = gtsam.Point3(radius*cos(theta), radius*sin(theta), 0)
@ -31,4 +31,4 @@ def circlePose3(numPoses = 8, radius = 1.0, symbolChar = 0):
gTi = gtsam.Pose3(gRo.compose(oRi), gti) gTi = gtsam.Pose3(gRo.compose(oRi), gti)
values.insert(key, gTi) values.insert(key, gTi)
theta = theta + dtheta theta = theta + dtheta
return values return values

View File

@ -1,27 +0,0 @@
import numpy as np
def Vector(*args):
"""
Convenient function to create numpy vector to use with gtsam cython wrapper
Usage: Vector(1), Vector(1,2,3), Vector(3,2,4)
"""
ret = np.squeeze(np.asarray(args, dtype='float'))
if ret.ndim == 0:
ret = np.expand_dims(ret, axis=0)
return ret
def Matrix(*args):
"""
Convenient function to create numpy matrix to use with gtsam cython wrapper
Usage: Matrix([1])
Matrix([1,2,3])
Matrix((3,2,4))
Matrix([1,2,3],[2,3,4])
Matrix((1,2,3),(2,3,4))
"""
ret = np.asarray(args, dtype='float', order='F')
if ret.ndim == 1:
ret = np.expand_dims(ret, axis=0)
return ret

View File

@ -59,11 +59,11 @@ class Data:
# Set Noise parameters # Set Noise parameters
self.noiseModels = Data.NoiseModels() self.noiseModels = Data.NoiseModels()
self.noiseModels.posePrior = noiseModel_Diagonal.Sigmas( self.noiseModels.posePrior = noiseModel_Diagonal.Sigmas(
Vector([0.001, 0.001, 0.001, 0.1, 0.1, 0.1])) np.array([0.001, 0.001, 0.001, 0.1, 0.1, 0.1]))
# noiseModels.odometry = noiseModel_Diagonal.Sigmas( # noiseModels.odometry = noiseModel_Diagonal.Sigmas(
# Vector([0.001,0.001,0.001,0.1,0.1,0.1])) # np.array([0.001,0.001,0.001,0.1,0.1,0.1]))
self.noiseModels.odometry = noiseModel_Diagonal.Sigmas( self.noiseModels.odometry = noiseModel_Diagonal.Sigmas(
Vector([0.05, 0.05, 0.05, 0.2, 0.2, 0.2])) np.array([0.05, 0.05, 0.05, 0.2, 0.2, 0.2]))
self.noiseModels.pointPrior = noiseModel_Isotropic.Sigma(3, 0.1) self.noiseModels.pointPrior = noiseModel_Isotropic.Sigma(3, 0.1)
self.noiseModels.measurement = noiseModel_Isotropic.Sigma(2, 1.0) self.noiseModels.measurement = noiseModel_Isotropic.Sigma(2, 1.0)
@ -84,8 +84,7 @@ def generate_data(options):
r = 10 r = 10
for j in range(len(truth.points)): for j in range(len(truth.points)):
theta = j * 2 * pi / nrPoints theta = j * 2 * pi / nrPoints
truth.points[j] = Point3( truth.points[j] = Point3(r * cos(theta), r * sin(theta), 0)
v=Vector([r * cos(theta), r * sin(theta), 0]))
else: # 3D landmarks as vertices of a cube else: # 3D landmarks as vertices of a cube
truth.points = [Point3(10, 10, 10), truth.points = [Point3(10, 10, 10),
Point3(-10, 10, 10), Point3(-10, 10, 10),
@ -101,9 +100,9 @@ def generate_data(options):
r = 40 r = 40
for i in range(options.nrCameras): for i in range(options.nrCameras):
theta = i * 2 * pi / options.nrCameras theta = i * 2 * pi / options.nrCameras
t = Point3(Vector(r * cos(theta), r * sin(theta), height)) t = Point3(r * cos(theta), r * sin(theta), height)
truth.cameras[i] = SimpleCamera.Lookat( truth.cameras[i] = SimpleCamera.Lookat(
t, Point3(), Point3(Vector(0, 0, 1)), truth.K) t, Point3(), Point3(0, 0, 1), truth.K)
# Create measurements # Create measurements
for j in range(nrPoints): for j in range(nrPoints):
# All landmarks seen in every frame # All landmarks seen in every frame