Beispiel #1
0
def generate_figure(z, num_samples, empty=False, disable={}, verbose=True):
    degree, num_control_points, dim, is_closed = (z['degree'],
                                                  z['num_control_points'],
                                                  z['dim'], z['is_closed'])

    if verbose:
        print('  degree:', degree)
        print('  num_control_points:', num_control_points)
        print('  dim:', dim)
        print('  is_closed:', is_closed)
    c = UniformBSpline(degree, num_control_points, dim, is_closed=is_closed)

    Y, w, u, X = [np.array(z[k]) for k in 'YwuX']
    if verbose:
        print('  num_data_points:', Y.shape[0])

    kw = {}
    if Y.shape[1] == 3:
        kw['projection'] = '3d'
    f = plt.figure()
    if empty:
        ax = f.add_axes((0, 0, 1, 1), **kw)
        ax.set_xticks([])
        ax.set_yticks([])
        if Y.shape[1] == 3:
            ax.set_zticks([])
        ax.xaxis.set_visible(False)
        ax.yaxis.set_visible(False)
        for spine in ax.spines.values():
            spine.set_visible(False)
    else:
        ax = f.add_subplot(111, **kw)
    ax.set_aspect('equal')

    def plot(X, *args, **kwargs):
        ax.plot(*(tuple(X.T) + args), **kwargs)

    if 'Y' not in disable:
        plot(Y, '.', c=C['r'])

    if 'u' not in disable:
        for m, y in zip(c.M(u, X), Y):
            plot(np.r_['0,2', m, y], '-', c=C['o'])

    if 'X' not in disable:
        plot(X, 'o--', ms=6.0, c='k', mec='k')
    if 'M' not in disable:
        plot(c.M(c.uniform_parameterisation(num_samples), X),
             '-',
             c=C['b'],
             lw=3.0)

    if not empty:
        e = z.get('e')
        if e is not None:
            ax.set_title('Energy: {:.7e}'.format(e))

    return f
def main():
    parser = argparse.ArgumentParser()
    parser.add_argument('num_data_points', type=int)
    parser.add_argument('w', type=float_tuple)
    parser.add_argument('lambda_', type=float)
    parser.add_argument('degree', type=int)
    parser.add_argument('num_control_points', type=int)
    parser.add_argument('output_path')
    parser.add_argument('--alpha', type=float, default=1.0 / (2.0 * np.pi))
    parser.add_argument('--dim', type=int, choices={2, 3}, default=2)
    parser.add_argument('--frequency', type=float, default=1.0)
    parser.add_argument('--num-init-points', type=int, default=16)
    parser.add_argument('--sigma', type=float, default=0.05)
    parser.add_argument('--seed', type=int)
    args = parser.parse_args()

    print('Parameters:')
    print('  alpha:', args.alpha)
    print('  frequency:', args.frequency)
    x = np.linspace(0.0, 2.0 * np.pi, args.num_data_points)
    y = np.exp(-args.alpha * x) * np.sin(args.frequency * x)

    if args.dim == 2:
        Y = np.c_[x, y]
    else:
        Y = np.c_[x, y, np.linspace(0.0, 1.0, args.num_data_points)]

    # Initialise `X` so that the uniform B-spline linearly interpolates between
    # the first and last noise-free data points.
    t = np.linspace(0.0, 1.0, args.num_control_points)[:, np.newaxis]
    X = Y[0] * (1 - t) + Y[-1] * t

    c = UniformBSpline(args.degree, args.num_control_points, args.dim)
    m0, m1 = c.M(c.uniform_parameterisation(2), X)
    x01 = 0.5 * (X[0] + X[-1])
    X = (np.linalg.norm(Y[0] - Y[-1]) / np.linalg.norm(m1 - m0)) * (X -
                                                                    x01) + x01

    # Add isotropic zero-mean Gaussian noise to the data.
    if args.seed is not None:
        print('  seed:', args.seed)
        np.random.seed(args.seed)
    print('  sigma:', args.sigma)
    Y += args.sigma * np.random.randn(Y.size).reshape(Y.shape)

    # Set `w`.
    if np.any(np.asarray(args.w) < 0):
        raise ValueError('w <= 0.0 (= {})'.format(args.w))
    if len(args.w) == 1:
        w = np.empty((args.num_data_points, args.dim), dtype=float)
        w.fill(args.w[0])
    elif len(args.w) == args.dim:
        w = np.tile(args.w, (args.num_data_points, 1))
    else:
        raise ValueError('len(w) is invalid (= {})'.format(len(args.w)))

    # Check `lambda_`.
    if args.lambda_ <= 0.0:
        raise ValueError('lambda_ <= 0.0 (= {})'.format(args.lambda_))

    # Initialise `u`.
    u0 = c.uniform_parameterisation(args.num_init_points)
    D = scipy.spatial.distance.cdist(Y, c.M(u0, X))
    u = u0[D.argmin(axis=1)]

    # Save.
    to_list = lambda _: _.tolist()
    z = dict(degree=args.degree,
             num_control_points=args.num_control_points,
             dim=args.dim,
             is_closed=False,
             Y=to_list(Y),
             w=to_list(w),
             lambda_=args.lambda_,
             u=to_list(u),
             X=to_list(X))

    print('Output:', args.output_path)
    with open(args.output_path, 'w') as fp:
        fp.write(json.dumps(z, indent=4))
def fit_bspline(x,
                y,
                dim=2,
                degree=2,
                num_control_points=20,
                is_closed=False,
                num_init_points=1000):
    ''' Fits and returns a bspline curve to the given x and y points
    
        Parameters
        ----------
        x : list
            data x-coordinates
        y : list
            data y-coordinates
        dim : int
            the dimensionality of the dataset (default: 2)
        degree : int
            the degree of the b-spline polynomial (default: 2)
        num_control_points : int
            the number of b-spline control points (default: 20)
        is_closed : boolean
            should the b-spline be closed? (default: false)
        num_init_points : int
            number of initial points to use in the b-spline parameterization
            when starting the regression. (default: 1000)
        
        Returns
        -------
        c: a UniformBSpline object containing the optimized b-spline
    '''
    # TODO: extract dimensionality from the x,y dataset itself
    num_data_points = len(x)
    c = UniformBSpline(degree, num_control_points, dim, is_closed=is_closed)
    Y = np.c_[x, y]  # Data num_points by dimension
    # Now we need weights for all of the data points
    w = np.empty((num_data_points, dim), dtype=float)
    # Currently, every point is equally important
    w.fill(1)  # Uniform weight to the different points
    # Initialize `X` so that the uniform B-spline linearly interpolates between
    # the first and last noise-free data points.
    t = np.linspace(0.0, 1.0, num_control_points)[:, np.newaxis]
    X = Y[0] * (1 - t) + Y[-1] * t
    # NOTE: Not entirely sure if the next three lines are necessary or not
    m0, m1 = c.M(c.uniform_parameterisation(2), X)
    x01 = 0.5 * (X[0] + X[-1])
    X = (np.linalg.norm(Y[0] - Y[-1]) / np.linalg.norm(m1 - m0)) * (X -
                                                                    x01) + x01
    # Regularization weight on the control point distance
    # This specifies a penalty on having the b-spline control points close
    # together, and in some sense prevents over-fitting. Change this if the
    # curve doesn't capture the curve variation well or smoothly enough
    lambda_ = 0.5
    # These parameters affect the regression solver.
    # Presently, they are disabled below, but you can think about enabling them
    # if that would be useful for your use case.
    #    max_num_iterations = 1000
    #    min_radius = 0
    #    max_radius = 400
    #    initial_radius = 100
    # Initialize U
    u0 = c.uniform_parameterisation(num_init_points)
    D = cdist(Y, c.M(u0, X))
    u = u0[D.argmin(axis=1)]
    # Run the solver
    (
        u, X, has_converged, states, num_iterations, time_taken
    ) = UniformBSplineLeastSquaresOptimiser(c, 'lm').minimise(
        Y,
        w,
        lambda_,
        u,
        X,
        #max_num_iterations = max_num_iterations,
        #min_radius = min_radius,
        #max_radius = max_radius,
        #initial_radius = initial_radius,
        return_all=True)
    return c, u0, X