Starting Week 9 assignment

This commit is contained in:
julien Lengrand-Lambert
2016-01-03 11:03:54 +01:00
parent 1f3bea0684
commit a22ee5d3a3
33 changed files with 5465 additions and 0 deletions

BIN
machine-learning-ex8.zip Normal file

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,48 @@
function checkCostFunction(lambda)
%CHECKCOSTFUNCTION Creates a collaborative filering problem
%to check your cost function and gradients
% CHECKCOSTFUNCTION(lambda) Creates a collaborative filering problem
% to check your cost function and gradients, it will output the
% analytical gradients produced by your code and the numerical gradients
% (computed using computeNumericalGradient). These two gradient
% computations should result in very similar values.
% Set lambda
if ~exist('lambda', 'var') || isempty(lambda)
lambda = 0;
end
%% Create small problem
X_t = rand(4, 3);
Theta_t = rand(5, 3);
% Zap out most entries
Y = X_t * Theta_t';
Y(rand(size(Y)) > 0.5) = 0;
R = zeros(size(Y));
R(Y ~= 0) = 1;
%% Run Gradient Checking
X = randn(size(X_t));
Theta = randn(size(Theta_t));
num_users = size(Y, 2);
num_movies = size(Y, 1);
num_features = size(Theta_t, 2);
numgrad = computeNumericalGradient( ...
@(t) cofiCostFunc(t, Y, R, num_users, num_movies, ...
num_features, lambda), [X(:); Theta(:)]);
[cost, grad] = cofiCostFunc([X(:); Theta(:)], Y, R, num_users, ...
num_movies, num_features, lambda);
disp([numgrad grad]);
fprintf(['The above two columns you get should be very similar.\n' ...
'(Left-Your Numerical Gradient, Right-Analytical Gradient)\n\n']);
diff = norm(numgrad-grad)/norm(numgrad+grad);
fprintf(['If your backpropagation implementation is correct, then \n' ...
'the relative difference will be small (less than 1e-9). \n' ...
'\nRelative Difference: %g\n'], diff);
end

View File

@@ -0,0 +1,62 @@
function [J, grad] = cofiCostFunc(params, Y, R, num_users, num_movies, ...
num_features, lambda)
%COFICOSTFUNC Collaborative filtering cost function
% [J, grad] = COFICOSTFUNC(params, Y, R, num_users, num_movies, ...
% num_features, lambda) returns the cost and gradient for the
% collaborative filtering problem.
%
% Unfold the U and W matrices from params
X = reshape(params(1:num_movies*num_features), num_movies, num_features);
Theta = reshape(params(num_movies*num_features+1:end), ...
num_users, num_features);
% You need to return the following values correctly
J = 0;
X_grad = zeros(size(X));
Theta_grad = zeros(size(Theta));
% ====================== YOUR CODE HERE ======================
% Instructions: Compute the cost function and gradient for collaborative
% filtering. Concretely, you should first implement the cost
% function (without regularization) and make sure it is
% matches our costs. After that, you should implement the
% gradient and use the checkCostFunction routine to check
% that the gradient is correct. Finally, you should implement
% regularization.
%
% Notes: X - num_movies x num_features matrix of movie features
% Theta - num_users x num_features matrix of user features
% Y - num_movies x num_users matrix of user ratings of movies
% R - num_movies x num_users matrix, where R(i, j) = 1 if the
% i-th movie was rated by the j-th user
%
% You should set the following variables correctly:
%
% X_grad - num_movies x num_features matrix, containing the
% partial derivatives w.r.t. to each element of X
% Theta_grad - num_users x num_features matrix, containing the
% partial derivatives w.r.t. to each element of Theta
%
% =============================================================
grad = [X_grad(:); Theta_grad(:)];
end

View File

@@ -0,0 +1,29 @@
function numgrad = computeNumericalGradient(J, theta)
%COMPUTENUMERICALGRADIENT Computes the gradient using "finite differences"
%and gives us a numerical estimate of the gradient.
% numgrad = COMPUTENUMERICALGRADIENT(J, theta) computes the numerical
% gradient of the function J around theta. Calling y = J(theta) should
% return the function value at theta.
% Notes: The following code implements numerical gradient checking, and
% returns the numerical gradient.It sets numgrad(i) to (a numerical
% approximation of) the partial derivative of J with respect to the
% i-th input argument, evaluated at theta. (i.e., numgrad(i) should
% be the (approximately) the partial derivative of J with respect
% to theta(i).)
%
numgrad = zeros(size(theta));
perturb = zeros(size(theta));
e = 1e-4;
for p = 1:numel(theta)
% Set perturbation vector
perturb(p) = e;
loss1 = J(theta - perturb);
loss2 = J(theta + perturb);
% Compute Numerical Gradient
numgrad(p) = (loss2 - loss1) / (2*e);
perturb(p) = 0;
end
end

View File

@@ -0,0 +1,36 @@
function [mu sigma2] = estimateGaussian(X)
%ESTIMATEGAUSSIAN This function estimates the parameters of a
%Gaussian distribution using the data in X
% [mu sigma2] = estimateGaussian(X),
% The input X is the dataset with each n-dimensional data point in one row
% The output is an n-dimensional vector mu, the mean of the data set
% and the variances sigma^2, an n x 1 vector
%
% Useful variables
[m, n] = size(X);
% You should return these values correctly
mu = zeros(n, 1);
sigma2 = zeros(n, 1);
% ====================== YOUR CODE HERE ======================
% Instructions: Compute the mean of the data and the variances
% In particular, mu(i) should contain the mean of
% the data for the i-th feature and sigma2(i)
% should contain variance of the i-th feature.
%
% =============================================================
end

View File

@@ -0,0 +1,123 @@
%% Machine Learning Online Class
% Exercise 8 | Anomaly Detection and Collaborative Filtering
%
% Instructions
% ------------
%
% This file contains code that helps you get started on the
% exercise. You will need to complete the following functions:
%
% estimateGaussian.m
% selectThreshold.m
% cofiCostFunc.m
%
% For this exercise, you will not need to change any code in this file,
% or any other files other than those mentioned above.
%
%% Initialization
clear ; close all; clc
%% ================== Part 1: Load Example Dataset ===================
% We start this exercise by using a small dataset that is easy to
% visualize.
%
% Our example case consists of 2 network server statistics across
% several machines: the latency and throughput of each machine.
% This exercise will help us find possibly faulty (or very fast) machines.
%
fprintf('Visualizing example dataset for outlier detection.\n\n');
% The following command loads the dataset. You should now have the
% variables X, Xval, yval in your environment
load('ex8data1.mat');
% Visualize the example dataset
plot(X(:, 1), X(:, 2), 'bx');
axis([0 30 0 30]);
xlabel('Latency (ms)');
ylabel('Throughput (mb/s)');
fprintf('Program paused. Press enter to continue.\n');
pause
%% ================== Part 2: Estimate the dataset statistics ===================
% For this exercise, we assume a Gaussian distribution for the dataset.
%
% We first estimate the parameters of our assumed Gaussian distribution,
% then compute the probabilities for each of the points and then visualize
% both the overall distribution and where each of the points falls in
% terms of that distribution.
%
fprintf('Visualizing Gaussian fit.\n\n');
% Estimate my and sigma2
[mu sigma2] = estimateGaussian(X);
% Returns the density of the multivariate normal at each data point (row)
% of X
p = multivariateGaussian(X, mu, sigma2);
% Visualize the fit
visualizeFit(X, mu, sigma2);
xlabel('Latency (ms)');
ylabel('Throughput (mb/s)');
fprintf('Program paused. Press enter to continue.\n');
pause;
%% ================== Part 3: Find Outliers ===================
% Now you will find a good epsilon threshold using a cross-validation set
% probabilities given the estimated Gaussian distribution
%
pval = multivariateGaussian(Xval, mu, sigma2);
[epsilon F1] = selectThreshold(yval, pval);
fprintf('Best epsilon found using cross-validation: %e\n', epsilon);
fprintf('Best F1 on Cross Validation Set: %f\n', F1);
fprintf(' (you should see a value epsilon of about 8.99e-05)\n\n');
% Find the outliers in the training set and plot the
outliers = find(p < epsilon);
% Draw a red circle around those outliers
hold on
plot(X(outliers, 1), X(outliers, 2), 'ro', 'LineWidth', 2, 'MarkerSize', 10);
hold off
fprintf('Program paused. Press enter to continue.\n');
pause;
%% ================== Part 4: Multidimensional Outliers ===================
% We will now use the code from the previous part and apply it to a
% harder problem in which more features describe each datapoint and only
% some features indicate whether a point is an outlier.
%
% Loads the second dataset. You should now have the
% variables X, Xval, yval in your environment
load('ex8data2.mat');
% Apply the same steps to the larger dataset
[mu sigma2] = estimateGaussian(X);
% Training set
p = multivariateGaussian(X, mu, sigma2);
% Cross-validation set
pval = multivariateGaussian(Xval, mu, sigma2);
% Find the best threshold
[epsilon F1] = selectThreshold(yval, pval);
fprintf('Best epsilon found using cross-validation: %e\n', epsilon);
fprintf('Best F1 on Cross Validation Set: %f\n', F1);
fprintf('# Outliers found: %d\n', sum(p < epsilon));
fprintf(' (you should see a value epsilon of about 1.38e-18)\n\n');
pause

View File

@@ -0,0 +1,237 @@
%% Machine Learning Online Class
% Exercise 8 | Anomaly Detection and Collaborative Filtering
%
% Instructions
% ------------
%
% This file contains code that helps you get started on the
% exercise. You will need to complete the following functions:
%
% estimateGaussian.m
% selectThreshold.m
% cofiCostFunc.m
%
% For this exercise, you will not need to change any code in this file,
% or any other files other than those mentioned above.
%
%% =============== Part 1: Loading movie ratings dataset ================
% You will start by loading the movie ratings dataset to understand the
% structure of the data.
%
fprintf('Loading movie ratings dataset.\n\n');
% Load data
load ('ex8_movies.mat');
% Y is a 1682x943 matrix, containing ratings (1-5) of 1682 movies on
% 943 users
%
% R is a 1682x943 matrix, where R(i,j) = 1 if and only if user j gave a
% rating to movie i
% From the matrix, we can compute statistics like average rating.
fprintf('Average rating for movie 1 (Toy Story): %f / 5\n\n', ...
mean(Y(1, R(1, :))));
% We can "visualize" the ratings matrix by plotting it with imagesc
imagesc(Y);
ylabel('Movies');
xlabel('Users');
fprintf('\nProgram paused. Press enter to continue.\n');
pause;
%% ============ Part 2: Collaborative Filtering Cost Function ===========
% You will now implement the cost function for collaborative filtering.
% To help you debug your cost function, we have included set of weights
% that we trained on that. Specifically, you should complete the code in
% cofiCostFunc.m to return J.
% Load pre-trained weights (X, Theta, num_users, num_movies, num_features)
load ('ex8_movieParams.mat');
% Reduce the data set size so that this runs faster
num_users = 4; num_movies = 5; num_features = 3;
X = X(1:num_movies, 1:num_features);
Theta = Theta(1:num_users, 1:num_features);
Y = Y(1:num_movies, 1:num_users);
R = R(1:num_movies, 1:num_users);
% Evaluate cost function
J = cofiCostFunc([X(:) ; Theta(:)], Y, R, num_users, num_movies, ...
num_features, 0);
fprintf(['Cost at loaded parameters: %f '...
'\n(this value should be about 22.22)\n'], J);
fprintf('\nProgram paused. Press enter to continue.\n');
pause;
%% ============== Part 3: Collaborative Filtering Gradient ==============
% Once your cost function matches up with ours, you should now implement
% the collaborative filtering gradient function. Specifically, you should
% complete the code in cofiCostFunc.m to return the grad argument.
%
fprintf('\nChecking Gradients (without regularization) ... \n');
% Check gradients by running checkNNGradients
checkCostFunction;
fprintf('\nProgram paused. Press enter to continue.\n');
pause;
%% ========= Part 4: Collaborative Filtering Cost Regularization ========
% Now, you should implement regularization for the cost function for
% collaborative filtering. You can implement it by adding the cost of
% regularization to the original cost computation.
%
% Evaluate cost function
J = cofiCostFunc([X(:) ; Theta(:)], Y, R, num_users, num_movies, ...
num_features, 1.5);
fprintf(['Cost at loaded parameters (lambda = 1.5): %f '...
'\n(this value should be about 31.34)\n'], J);
fprintf('\nProgram paused. Press enter to continue.\n');
pause;
%% ======= Part 5: Collaborative Filtering Gradient Regularization ======
% Once your cost matches up with ours, you should proceed to implement
% regularization for the gradient.
%
%
fprintf('\nChecking Gradients (with regularization) ... \n');
% Check gradients by running checkNNGradients
checkCostFunction(1.5);
fprintf('\nProgram paused. Press enter to continue.\n');
pause;
%% ============== Part 6: Entering ratings for a new user ===============
% Before we will train the collaborative filtering model, we will first
% add ratings that correspond to a new user that we just observed. This
% part of the code will also allow you to put in your own ratings for the
% movies in our dataset!
%
movieList = loadMovieList();
% Initialize my ratings
my_ratings = zeros(1682, 1);
% Check the file movie_idx.txt for id of each movie in our dataset
% For example, Toy Story (1995) has ID 1, so to rate it "4", you can set
my_ratings(1) = 4;
% Or suppose did not enjoy Silence of the Lambs (1991), you can set
my_ratings(98) = 2;
% We have selected a few movies we liked / did not like and the ratings we
% gave are as follows:
my_ratings(7) = 3;
my_ratings(12)= 5;
my_ratings(54) = 4;
my_ratings(64)= 5;
my_ratings(66)= 3;
my_ratings(69) = 5;
my_ratings(183) = 4;
my_ratings(226) = 5;
my_ratings(355)= 5;
fprintf('\n\nNew user ratings:\n');
for i = 1:length(my_ratings)
if my_ratings(i) > 0
fprintf('Rated %d for %s\n', my_ratings(i), ...
movieList{i});
end
end
fprintf('\nProgram paused. Press enter to continue.\n');
pause;
%% ================== Part 7: Learning Movie Ratings ====================
% Now, you will train the collaborative filtering model on a movie rating
% dataset of 1682 movies and 943 users
%
fprintf('\nTraining collaborative filtering...\n');
% Load data
load('ex8_movies.mat');
% Y is a 1682x943 matrix, containing ratings (1-5) of 1682 movies by
% 943 users
%
% R is a 1682x943 matrix, where R(i,j) = 1 if and only if user j gave a
% rating to movie i
% Add our own ratings to the data matrix
Y = [my_ratings Y];
R = [(my_ratings ~= 0) R];
% Normalize Ratings
[Ynorm, Ymean] = normalizeRatings(Y, R);
% Useful Values
num_users = size(Y, 2);
num_movies = size(Y, 1);
num_features = 10;
% Set Initial Parameters (Theta, X)
X = randn(num_movies, num_features);
Theta = randn(num_users, num_features);
initial_parameters = [X(:); Theta(:)];
% Set options for fmincg
options = optimset('GradObj', 'on', 'MaxIter', 100);
% Set Regularization
lambda = 10;
theta = fmincg (@(t)(cofiCostFunc(t, Y, R, num_users, num_movies, ...
num_features, lambda)), ...
initial_parameters, options);
% Unfold the returned theta back into U and W
X = reshape(theta(1:num_movies*num_features), num_movies, num_features);
Theta = reshape(theta(num_movies*num_features+1:end), ...
num_users, num_features);
fprintf('Recommender system learning completed.\n');
fprintf('\nProgram paused. Press enter to continue.\n');
pause;
%% ================== Part 8: Recommendation for you ====================
% After training the model, you can now make recommendations by computing
% the predictions matrix.
%
p = X * Theta';
my_predictions = p(:,1) + Ymean;
movieList = loadMovieList();
[r, ix] = sort(my_predictions, 'descend');
fprintf('\nTop recommendations for you:\n');
for i=1:10
j = ix(i);
fprintf('Predicting rating %.1f for movie %s\n', my_predictions(j), ...
movieList{j});
end
fprintf('\n\nOriginal ratings provided:\n');
for i = 1:length(my_ratings)
if my_ratings(i) > 0
fprintf('Rated %d for %s\n', my_ratings(i), ...
movieList{i});
end
end

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,175 @@
function [X, fX, i] = fmincg(f, X, options, P1, P2, P3, P4, P5)
% Minimize a continuous differentialble multivariate function. Starting point
% is given by "X" (D by 1), and the function named in the string "f", must
% return a function value and a vector of partial derivatives. The Polack-
% Ribiere flavour of conjugate gradients is used to compute search directions,
% and a line search using quadratic and cubic polynomial approximations and the
% Wolfe-Powell stopping criteria is used together with the slope ratio method
% for guessing initial step sizes. Additionally a bunch of checks are made to
% make sure that exploration is taking place and that extrapolation will not
% be unboundedly large. The "length" gives the length of the run: if it is
% positive, it gives the maximum number of line searches, if negative its
% absolute gives the maximum allowed number of function evaluations. You can
% (optionally) give "length" a second component, which will indicate the
% reduction in function value to be expected in the first line-search (defaults
% to 1.0). The function returns when either its length is up, or if no further
% progress can be made (ie, we are at a minimum, or so close that due to
% numerical problems, we cannot get any closer). If the function terminates
% within a few iterations, it could be an indication that the function value
% and derivatives are not consistent (ie, there may be a bug in the
% implementation of your "f" function). The function returns the found
% solution "X", a vector of function values "fX" indicating the progress made
% and "i" the number of iterations (line searches or function evaluations,
% depending on the sign of "length") used.
%
% Usage: [X, fX, i] = fmincg(f, X, options, P1, P2, P3, P4, P5)
%
% See also: checkgrad
%
% Copyright (C) 2001 and 2002 by Carl Edward Rasmussen. Date 2002-02-13
%
%
% (C) Copyright 1999, 2000 & 2001, Carl Edward Rasmussen
%
% Permission is granted for anyone to copy, use, or modify these
% programs and accompanying documents for purposes of research or
% education, provided this copyright notice is retained, and note is
% made of any changes that have been made.
%
% These programs and documents are distributed without any warranty,
% express or implied. As the programs were written for research
% purposes only, they have not been tested to the degree that would be
% advisable in any important application. All use of these programs is
% entirely at the user's own risk.
%
% [ml-class] Changes Made:
% 1) Function name and argument specifications
% 2) Output display
%
% Read options
if exist('options', 'var') && ~isempty(options) && isfield(options, 'MaxIter')
length = options.MaxIter;
else
length = 100;
end
RHO = 0.01; % a bunch of constants for line searches
SIG = 0.5; % RHO and SIG are the constants in the Wolfe-Powell conditions
INT = 0.1; % don't reevaluate within 0.1 of the limit of the current bracket
EXT = 3.0; % extrapolate maximum 3 times the current bracket
MAX = 20; % max 20 function evaluations per line search
RATIO = 100; % maximum allowed slope ratio
argstr = ['feval(f, X']; % compose string used to call function
for i = 1:(nargin - 3)
argstr = [argstr, ',P', int2str(i)];
end
argstr = [argstr, ')'];
if max(size(length)) == 2, red=length(2); length=length(1); else red=1; end
S=['Iteration '];
i = 0; % zero the run length counter
ls_failed = 0; % no previous line search has failed
fX = [];
[f1 df1] = eval(argstr); % get function value and gradient
i = i + (length<0); % count epochs?!
s = -df1; % search direction is steepest
d1 = -s'*s; % this is the slope
z1 = red/(1-d1); % initial step is red/(|s|+1)
while i < abs(length) % while not finished
i = i + (length>0); % count iterations?!
X0 = X; f0 = f1; df0 = df1; % make a copy of current values
X = X + z1*s; % begin line search
[f2 df2] = eval(argstr);
i = i + (length<0); % count epochs?!
d2 = df2'*s;
f3 = f1; d3 = d1; z3 = -z1; % initialize point 3 equal to point 1
if length>0, M = MAX; else M = min(MAX, -length-i); end
success = 0; limit = -1; % initialize quanteties
while 1
while ((f2 > f1+z1*RHO*d1) || (d2 > -SIG*d1)) && (M > 0)
limit = z1; % tighten the bracket
if f2 > f1
z2 = z3 - (0.5*d3*z3*z3)/(d3*z3+f2-f3); % quadratic fit
else
A = 6*(f2-f3)/z3+3*(d2+d3); % cubic fit
B = 3*(f3-f2)-z3*(d3+2*d2);
z2 = (sqrt(B*B-A*d2*z3*z3)-B)/A; % numerical error possible - ok!
end
if isnan(z2) || isinf(z2)
z2 = z3/2; % if we had a numerical problem then bisect
end
z2 = max(min(z2, INT*z3),(1-INT)*z3); % don't accept too close to limits
z1 = z1 + z2; % update the step
X = X + z2*s;
[f2 df2] = eval(argstr);
M = M - 1; i = i + (length<0); % count epochs?!
d2 = df2'*s;
z3 = z3-z2; % z3 is now relative to the location of z2
end
if f2 > f1+z1*RHO*d1 || d2 > -SIG*d1
break; % this is a failure
elseif d2 > SIG*d1
success = 1; break; % success
elseif M == 0
break; % failure
end
A = 6*(f2-f3)/z3+3*(d2+d3); % make cubic extrapolation
B = 3*(f3-f2)-z3*(d3+2*d2);
z2 = -d2*z3*z3/(B+sqrt(B*B-A*d2*z3*z3)); % num. error possible - ok!
if ~isreal(z2) || isnan(z2) || isinf(z2) || z2 < 0 % num prob or wrong sign?
if limit < -0.5 % if we have no upper limit
z2 = z1 * (EXT-1); % the extrapolate the maximum amount
else
z2 = (limit-z1)/2; % otherwise bisect
end
elseif (limit > -0.5) && (z2+z1 > limit) % extraplation beyond max?
z2 = (limit-z1)/2; % bisect
elseif (limit < -0.5) && (z2+z1 > z1*EXT) % extrapolation beyond limit
z2 = z1*(EXT-1.0); % set to extrapolation limit
elseif z2 < -z3*INT
z2 = -z3*INT;
elseif (limit > -0.5) && (z2 < (limit-z1)*(1.0-INT)) % too close to limit?
z2 = (limit-z1)*(1.0-INT);
end
f3 = f2; d3 = d2; z3 = -z2; % set point 3 equal to point 2
z1 = z1 + z2; X = X + z2*s; % update current estimates
[f2 df2] = eval(argstr);
M = M - 1; i = i + (length<0); % count epochs?!
d2 = df2'*s;
end % end of line search
if success % if line search succeeded
f1 = f2; fX = [fX' f1]';
fprintf('%s %4i | Cost: %4.6e\r', S, i, f1);
s = (df2'*df2-df1'*df2)/(df1'*df1)*s - df2; % Polack-Ribiere direction
tmp = df1; df1 = df2; df2 = tmp; % swap derivatives
d2 = df1'*s;
if d2 > 0 % new slope must be negative
s = -df1; % otherwise use steepest direction
d2 = -s'*s;
end
z1 = z1 * min(RATIO, d1/(d2-realmin)); % slope ratio but max RATIO
d1 = d2;
ls_failed = 0; % this line search did not fail
else
X = X0; f1 = f0; df1 = df0; % restore point from before failed line search
if ls_failed || i > abs(length) % line search failed twice in a row
break; % or we ran out of time, so we give up
end
tmp = df1; df1 = df2; df2 = tmp; % swap derivatives
s = -df1; % try steepest
d1 = -s'*s;
z1 = 1/(1-d1);
ls_failed = 1; % this line search failed
end
if exist('OCTAVE_VERSION')
fflush(stdout);
end
end
fprintf('\n');

View File

@@ -0,0 +1,41 @@
The author of "jsonlab" toolbox is Qianqian Fang. Qianqian
is currently an Assistant Professor at Massachusetts General Hospital,
Harvard Medical School.
Address: Martinos Center for Biomedical Imaging,
Massachusetts General Hospital,
Harvard Medical School
Bldg 149, 13th St, Charlestown, MA 02129, USA
URL: http://nmr.mgh.harvard.edu/~fangq/
Email: <fangq at nmr.mgh.harvard.edu> or <fangqq at gmail.com>
The script loadjson.m was built upon previous works by
- Nedialko Krouchev: http://www.mathworks.com/matlabcentral/fileexchange/25713
date: 2009/11/02
- François Glineur: http://www.mathworks.com/matlabcentral/fileexchange/23393
date: 2009/03/22
- Joel Feenstra: http://www.mathworks.com/matlabcentral/fileexchange/20565
date: 2008/07/03
This toolbox contains patches submitted by the following contributors:
- Blake Johnson <bjohnso at bbn.com>
part of revision 341
- Niclas Borlin <Niclas.Borlin at cs.umu.se>
various fixes in revision 394, including
- loadjson crashes for all-zero sparse matrix.
- loadjson crashes for empty sparse matrix.
- Non-zero size of 0-by-N and N-by-0 empty matrices is lost after savejson/loadjson.
- loadjson crashes for sparse real column vector.
- loadjson crashes for sparse complex column vector.
- Data is corrupted by savejson for sparse real row vector.
- savejson crashes for sparse complex row vector.
- Yul Kang <yul.kang.on at gmail.com>
patches for svn revision 415.
- savejson saves an empty cell array as [] instead of null
- loadjson differentiates an empty struct from an empty array

View File

@@ -0,0 +1,74 @@
============================================================================
JSONlab - a toolbox to encode/decode JSON/UBJSON files in MATLAB/Octave
----------------------------------------------------------------------------
JSONlab ChangeLog (key features marked by *):
== JSONlab 1.0 (codename: Optimus - Final), FangQ <fangq (at) nmr.mgh.harvard.edu> ==
2015/01/02 polish help info for all major functions, update examples, finalize 1.0
2014/12/19 fix a bug to strictly respect NoRowBracket in savejson
== JSONlab 1.0.0-RC2 (codename: Optimus - RC2), FangQ <fangq (at) nmr.mgh.harvard.edu> ==
2014/11/22 show progress bar in loadjson ('ShowProgress')
2014/11/17 add Compact option in savejson to output compact JSON format ('Compact')
2014/11/17 add FastArrayParser in loadjson to specify fast parser applicable levels
2014/09/18 start official github mirror: https://github.com/fangq/jsonlab
== JSONlab 1.0.0-RC1 (codename: Optimus - RC1), FangQ <fangq (at) nmr.mgh.harvard.edu> ==
2014/09/17 fix several compatibility issues when running on octave versions 3.2-3.8
2014/09/17 support 2D cell and struct arrays in both savejson and saveubjson
2014/08/04 escape special characters in a JSON string
2014/02/16 fix a bug when saving ubjson files
== JSONlab 0.9.9 (codename: Optimus - beta), FangQ <fangq (at) nmr.mgh.harvard.edu> ==
2014/01/22 use binary read and write in saveubjson and loadubjson
== JSONlab 0.9.8-1 (codename: Optimus - alpha update 1), FangQ <fangq (at) nmr.mgh.harvard.edu> ==
2013/10/07 better round-trip conservation for empty arrays and structs (patch submitted by Yul Kang)
== JSONlab 0.9.8 (codename: Optimus - alpha), FangQ <fangq (at) nmr.mgh.harvard.edu> ==
2013/08/23 *universal Binary JSON (UBJSON) support, including both saveubjson and loadubjson
== JSONlab 0.9.1 (codename: Rodimus, update 1), FangQ <fangq (at) nmr.mgh.harvard.edu> ==
2012/12/18 *handling of various empty and sparse matrices (fixes submitted by Niclas Borlin)
== JSONlab 0.9.0 (codename: Rodimus), FangQ <fangq (at) nmr.mgh.harvard.edu> ==
2012/06/17 *new format for an invalid leading char, unpacking hex code in savejson
2012/06/01 support JSONP in savejson
2012/05/25 fix the empty cell bug (reported by Cyril Davin)
2012/04/05 savejson can save to a file (suggested by Patrick Rapin)
== JSONlab 0.8.1 (codename: Sentiel, Update 1), FangQ <fangq (at) nmr.mgh.harvard.edu> ==
2012/02/28 loadjson quotation mark escape bug, see http://bit.ly/yyk1nS
2012/01/25 patch to handle root-less objects, contributed by Blake Johnson
== JSONlab 0.8.0 (codename: Sentiel), FangQ <fangq (at) nmr.mgh.harvard.edu> ==
2012/01/13 *speed up loadjson by 20 fold when parsing large data arrays in matlab
2012/01/11 remove row bracket if an array has 1 element, suggested by Mykel Kochenderfer
2011/12/22 *accept sequence of 'param',value input in savejson and loadjson
2011/11/18 fix struct array bug reported by Mykel Kochenderfer
== JSONlab 0.5.1 (codename: Nexus Update 1), FangQ <fangq (at) nmr.mgh.harvard.edu> ==
2011/10/21 fix a bug in loadjson, previous code does not use any of the acceleration
2011/10/20 loadjson supports JSON collections - concatenated JSON objects
== JSONlab 0.5.0 (codename: Nexus), FangQ <fangq (at) nmr.mgh.harvard.edu> ==
2011/10/16 package and release jsonlab 0.5.0
2011/10/15 *add json demo and regression test, support cpx numbers, fix double quote bug
2011/10/11 *speed up readjson dramatically, interpret _Array* tags, show data in root level
2011/10/10 create jsonlab project, start jsonlab website, add online documentation
2011/10/07 *speed up savejson by 25x using sprintf instead of mat2str, add options support
2011/10/06 *savejson works for structs, cells and arrays
2011/09/09 derive loadjson from JSON parser from MATLAB Central, draft savejson.m

View File

@@ -0,0 +1,25 @@
Copyright 2011-2015 Qianqian Fang <fangq at nmr.mgh.harvard.edu>. All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are
permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of
conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list
of conditions and the following disclaimer in the documentation and/or other materials
provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY EXPRESS OR IMPLIED
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
The views and conclusions contained in the software and documentation are those of the
authors and should not be interpreted as representing official policies, either expressed
or implied, of the copyright holders.

View File

@@ -0,0 +1,394 @@
===============================================================================
= JSONLab =
= An open-source MATLAB/Octave JSON encoder and decoder =
===============================================================================
*Copyright (C) 2011-2015 Qianqian Fang <fangq at nmr.mgh.harvard.edu>
*License: BSD License, see License_BSD.txt for details
*Version: 1.0 (Optimus - Final)
-------------------------------------------------------------------------------
Table of Content:
I. Introduction
II. Installation
III.Using JSONLab
IV. Known Issues and TODOs
V. Contribution and feedback
-------------------------------------------------------------------------------
I. Introduction
JSON ([http://www.json.org/ JavaScript Object Notation]) is a highly portable,
human-readable and "[http://en.wikipedia.org/wiki/JSON fat-free]" text format
to represent complex and hierarchical data. It is as powerful as
[http://en.wikipedia.org/wiki/XML XML], but less verbose. JSON format is widely
used for data-exchange in applications, and is essential for the wild success
of [http://en.wikipedia.org/wiki/Ajax_(programming) Ajax] and
[http://en.wikipedia.org/wiki/Web_2.0 Web2.0].
UBJSON (Universal Binary JSON) is a binary JSON format, specifically
optimized for compact file size and better performance while keeping
the semantics as simple as the text-based JSON format. Using the UBJSON
format allows to wrap complex binary data in a flexible and extensible
structure, making it possible to process complex and large dataset
without accuracy loss due to text conversions.
We envision that both JSON and its binary version will serve as part of
the mainstream data-exchange formats for scientific research in the future.
It will provide the flexibility and generality achieved by other popular
general-purpose file specifications, such as
[http://www.hdfgroup.org/HDF5/whatishdf5.html HDF5], with significantly
reduced complexity and enhanced performance.
JSONLab is a free and open-source implementation of a JSON/UBJSON encoder
and a decoder in the native MATLAB language. It can be used to convert a MATLAB
data structure (array, struct, cell, struct array and cell array) into
JSON/UBJSON formatted strings, or to decode a JSON/UBJSON file into MATLAB
data structure. JSONLab supports both MATLAB and
[http://www.gnu.org/software/octave/ GNU Octave] (a free MATLAB clone).
-------------------------------------------------------------------------------
II. Installation
The installation of JSONLab is no different than any other simple
MATLAB toolbox. You only need to download/unzip the JSONLab package
to a folder, and add the folder's path to MATLAB/Octave's path list
by using the following command:
addpath('/path/to/jsonlab');
If you want to add this path permanently, you need to type "pathtool",
browse to the jsonlab root folder and add to the list, then click "Save".
Then, run "rehash" in MATLAB, and type "which loadjson", if you see an
output, that means JSONLab is installed for MATLAB/Octave.
-------------------------------------------------------------------------------
III.Using JSONLab
JSONLab provides two functions, loadjson.m -- a MATLAB->JSON decoder,
and savejson.m -- a MATLAB->JSON encoder, for the text-based JSON, and
two equivallent functions -- loadubjson and saveubjson for the binary
JSON. The detailed help info for the four functions can be found below:
=== loadjson.m ===
<pre>
data=loadjson(fname,opt)
or
data=loadjson(fname,'param1',value1,'param2',value2,...)
parse a JSON (JavaScript Object Notation) file or string
authors:Qianqian Fang (fangq<at> nmr.mgh.harvard.edu)
created on 2011/09/09, including previous works from
Nedialko Krouchev: http://www.mathworks.com/matlabcentral/fileexchange/25713
created on 2009/11/02
François Glineur: http://www.mathworks.com/matlabcentral/fileexchange/23393
created on 2009/03/22
Joel Feenstra:
http://www.mathworks.com/matlabcentral/fileexchange/20565
created on 2008/07/03
$Id: loadjson.m 452 2014-11-22 16:43:33Z fangq $
input:
fname: input file name, if fname contains "{}" or "[]", fname
will be interpreted as a JSON string
opt: a struct to store parsing options, opt can be replaced by
a list of ('param',value) pairs - the param string is equivallent
to a field in opt. opt can have the following
fields (first in [.|.] is the default)
opt.SimplifyCell [0|1]: if set to 1, loadjson will call cell2mat
for each element of the JSON data, and group
arrays based on the cell2mat rules.
opt.FastArrayParser [1|0 or integer]: if set to 1, use a
speed-optimized array parser when loading an
array object. The fast array parser may
collapse block arrays into a single large
array similar to rules defined in cell2mat; 0 to
use a legacy parser; if set to a larger-than-1
value, this option will specify the minimum
dimension to enable the fast array parser. For
example, if the input is a 3D array, setting
FastArrayParser to 1 will return a 3D array;
setting to 2 will return a cell array of 2D
arrays; setting to 3 will return to a 2D cell
array of 1D vectors; setting to 4 will return a
3D cell array.
opt.ShowProgress [0|1]: if set to 1, loadjson displays a progress bar.
output:
dat: a cell array, where {...} blocks are converted into cell arrays,
and [...] are converted to arrays
examples:
dat=loadjson('{"obj":{"string":"value","array":[1,2,3]}}')
dat=loadjson(['examples' filesep 'example1.json'])
dat=loadjson(['examples' filesep 'example1.json'],'SimplifyCell',1)
</pre>
=== savejson.m ===
<pre>
json=savejson(rootname,obj,filename)
or
json=savejson(rootname,obj,opt)
json=savejson(rootname,obj,'param1',value1,'param2',value2,...)
convert a MATLAB object (cell, struct or array) into a JSON (JavaScript
Object Notation) string
author: Qianqian Fang (fangq<at> nmr.mgh.harvard.edu)
created on 2011/09/09
$Id: savejson.m 458 2014-12-19 22:17:17Z fangq $
input:
rootname: the name of the root-object, when set to '', the root name
is ignored, however, when opt.ForceRootName is set to 1 (see below),
the MATLAB variable name will be used as the root name.
obj: a MATLAB object (array, cell, cell array, struct, struct array).
filename: a string for the file name to save the output JSON data.
opt: a struct for additional options, ignore to use default values.
opt can have the following fields (first in [.|.] is the default)
opt.FileName [''|string]: a file name to save the output JSON data
opt.FloatFormat ['%.10g'|string]: format to show each numeric element
of a 1D/2D array;
opt.ArrayIndent [1|0]: if 1, output explicit data array with
precedent indentation; if 0, no indentation
opt.ArrayToStruct[0|1]: when set to 0, savejson outputs 1D/2D
array in JSON array format; if sets to 1, an
array will be shown as a struct with fields
"_ArrayType_", "_ArraySize_" and "_ArrayData_"; for
sparse arrays, the non-zero elements will be
saved to _ArrayData_ field in triplet-format i.e.
(ix,iy,val) and "_ArrayIsSparse_" will be added
with a value of 1; for a complex array, the
_ArrayData_ array will include two columns
(4 for sparse) to record the real and imaginary
parts, and also "_ArrayIsComplex_":1 is added.
opt.ParseLogical [0|1]: if this is set to 1, logical array elem
will use true/false rather than 1/0.
opt.NoRowBracket [1|0]: if this is set to 1, arrays with a single
numerical element will be shown without a square
bracket, unless it is the root object; if 0, square
brackets are forced for any numerical arrays.
opt.ForceRootName [0|1]: when set to 1 and rootname is empty, savejson
will use the name of the passed obj variable as the
root object name; if obj is an expression and
does not have a name, 'root' will be used; if this
is set to 0 and rootname is empty, the root level
will be merged down to the lower level.
opt.Inf ['"$1_Inf_"'|string]: a customized regular expression pattern
to represent +/-Inf. The matched pattern is '([-+]*)Inf'
and $1 represents the sign. For those who want to use
1e999 to represent Inf, they can set opt.Inf to '$11e999'
opt.NaN ['"_NaN_"'|string]: a customized regular expression pattern
to represent NaN
opt.JSONP [''|string]: to generate a JSONP output (JSON with padding),
for example, if opt.JSONP='foo', the JSON data is
wrapped inside a function call as 'foo(...);'
opt.UnpackHex [1|0]: conver the 0x[hex code] output by loadjson
back to the string form
opt.SaveBinary [0|1]: 1 - save the JSON file in binary mode; 0 - text mode.
opt.Compact [0|1]: 1- out compact JSON format (remove all newlines and tabs)
opt can be replaced by a list of ('param',value) pairs. The param
string is equivallent to a field in opt and is case sensitive.
output:
json: a string in the JSON format (see http://json.org)
examples:
jsonmesh=struct('MeshNode',[0 0 0;1 0 0;0 1 0;1 1 0;0 0 1;1 0 1;0 1 1;1 1 1],...
'MeshTetra',[1 2 4 8;1 3 4 8;1 2 6 8;1 5 6 8;1 5 7 8;1 3 7 8],...
'MeshTri',[1 2 4;1 2 6;1 3 4;1 3 7;1 5 6;1 5 7;...
2 8 4;2 8 6;3 8 4;3 8 7;5 8 6;5 8 7],...
'MeshCreator','FangQ','MeshTitle','T6 Cube',...
'SpecialData',[nan, inf, -inf]);
savejson('jmesh',jsonmesh)
savejson('',jsonmesh,'ArrayIndent',0,'FloatFormat','\t%.5g')
</pre>
=== loadubjson.m ===
<pre>
data=loadubjson(fname,opt)
or
data=loadubjson(fname,'param1',value1,'param2',value2,...)
parse a JSON (JavaScript Object Notation) file or string
authors:Qianqian Fang (fangq<at> nmr.mgh.harvard.edu)
created on 2013/08/01
$Id: loadubjson.m 436 2014-08-05 20:51:40Z fangq $
input:
fname: input file name, if fname contains "{}" or "[]", fname
will be interpreted as a UBJSON string
opt: a struct to store parsing options, opt can be replaced by
a list of ('param',value) pairs - the param string is equivallent
to a field in opt. opt can have the following
fields (first in [.|.] is the default)
opt.SimplifyCell [0|1]: if set to 1, loadubjson will call cell2mat
for each element of the JSON data, and group
arrays based on the cell2mat rules.
opt.IntEndian [B|L]: specify the endianness of the integer fields
in the UBJSON input data. B - Big-Endian format for
integers (as required in the UBJSON specification);
L - input integer fields are in Little-Endian order.
output:
dat: a cell array, where {...} blocks are converted into cell arrays,
and [...] are converted to arrays
examples:
obj=struct('string','value','array',[1 2 3]);
ubjdata=saveubjson('obj',obj);
dat=loadubjson(ubjdata)
dat=loadubjson(['examples' filesep 'example1.ubj'])
dat=loadubjson(['examples' filesep 'example1.ubj'],'SimplifyCell',1)
</pre>
=== saveubjson.m ===
<pre>
json=saveubjson(rootname,obj,filename)
or
json=saveubjson(rootname,obj,opt)
json=saveubjson(rootname,obj,'param1',value1,'param2',value2,...)
convert a MATLAB object (cell, struct or array) into a Universal
Binary JSON (UBJSON) binary string
author: Qianqian Fang (fangq<at> nmr.mgh.harvard.edu)
created on 2013/08/17
$Id: saveubjson.m 440 2014-09-17 19:59:45Z fangq $
input:
rootname: the name of the root-object, when set to '', the root name
is ignored, however, when opt.ForceRootName is set to 1 (see below),
the MATLAB variable name will be used as the root name.
obj: a MATLAB object (array, cell, cell array, struct, struct array)
filename: a string for the file name to save the output UBJSON data
opt: a struct for additional options, ignore to use default values.
opt can have the following fields (first in [.|.] is the default)
opt.FileName [''|string]: a file name to save the output JSON data
opt.ArrayToStruct[0|1]: when set to 0, saveubjson outputs 1D/2D
array in JSON array format; if sets to 1, an
array will be shown as a struct with fields
"_ArrayType_", "_ArraySize_" and "_ArrayData_"; for
sparse arrays, the non-zero elements will be
saved to _ArrayData_ field in triplet-format i.e.
(ix,iy,val) and "_ArrayIsSparse_" will be added
with a value of 1; for a complex array, the
_ArrayData_ array will include two columns
(4 for sparse) to record the real and imaginary
parts, and also "_ArrayIsComplex_":1 is added.
opt.ParseLogical [1|0]: if this is set to 1, logical array elem
will use true/false rather than 1/0.
opt.NoRowBracket [1|0]: if this is set to 1, arrays with a single
numerical element will be shown without a square
bracket, unless it is the root object; if 0, square
brackets are forced for any numerical arrays.
opt.ForceRootName [0|1]: when set to 1 and rootname is empty, saveubjson
will use the name of the passed obj variable as the
root object name; if obj is an expression and
does not have a name, 'root' will be used; if this
is set to 0 and rootname is empty, the root level
will be merged down to the lower level.
opt.JSONP [''|string]: to generate a JSONP output (JSON with padding),
for example, if opt.JSON='foo', the JSON data is
wrapped inside a function call as 'foo(...);'
opt.UnpackHex [1|0]: conver the 0x[hex code] output by loadjson
back to the string form
opt can be replaced by a list of ('param',value) pairs. The param
string is equivallent to a field in opt and is case sensitive.
output:
json: a binary string in the UBJSON format (see http://ubjson.org)
examples:
jsonmesh=struct('MeshNode',[0 0 0;1 0 0;0 1 0;1 1 0;0 0 1;1 0 1;0 1 1;1 1 1],...
'MeshTetra',[1 2 4 8;1 3 4 8;1 2 6 8;1 5 6 8;1 5 7 8;1 3 7 8],...
'MeshTri',[1 2 4;1 2 6;1 3 4;1 3 7;1 5 6;1 5 7;...
2 8 4;2 8 6;3 8 4;3 8 7;5 8 6;5 8 7],...
'MeshCreator','FangQ','MeshTitle','T6 Cube',...
'SpecialData',[nan, inf, -inf]);
saveubjson('jsonmesh',jsonmesh)
saveubjson('jsonmesh',jsonmesh,'meshdata.ubj')
</pre>
=== examples ===
Under the "examples" folder, you can find several scripts to demonstrate the
basic utilities of JSONLab. Running the "demo_jsonlab_basic.m" script, you
will see the conversions from MATLAB data structure to JSON text and backward.
In "jsonlab_selftest.m", we load complex JSON files downloaded from the Internet
and validate the loadjson/savejson functions for regression testing purposes.
Similarly, a "demo_ubjson_basic.m" script is provided to test the saveubjson
and loadubjson pairs for various matlab data structures.
Please run these examples and understand how JSONLab works before you use
it to process your data.
-------------------------------------------------------------------------------
IV. Known Issues and TODOs
JSONLab has several known limitations. We are striving to make it more general
and robust. Hopefully in a few future releases, the limitations become less.
Here are the known issues:
# 3D or higher dimensional cell/struct-arrays will be converted to 2D arrays;
# When processing names containing multi-byte characters, Octave and MATLAB \
can give different field-names; you can use feature('DefaultCharacterSet','latin1') \
in MATLAB to get consistant results
# savejson can not handle class and dataset.
# saveubjson converts a logical array into a uint8 ([U]) array
# an unofficial N-D array count syntax is implemented in saveubjson. We are \
actively communicating with the UBJSON spec maintainer to investigate the \
possibility of making it upstream
# loadubjson can not parse all UBJSON Specification (Draft 9) compliant \
files, however, it can parse all UBJSON files produced by saveubjson.
-------------------------------------------------------------------------------
V. Contribution and feedback
JSONLab is an open-source project. This means you can not only use it and modify
it as you wish, but also you can contribute your changes back to JSONLab so
that everyone else can enjoy the improvement. For anyone who want to contribute,
please download JSONLab source code from it's subversion repository by using the
following command:
svn checkout svn://svn.code.sf.net/p/iso2mesh/code/trunk/jsonlab jsonlab
You can make changes to the files as needed. Once you are satisfied with your
changes, and ready to share it with others, please cd the root directory of
JSONLab, and type
svn diff > yourname_featurename.patch
You then email the .patch file to JSONLab's maintainer, Qianqian Fang, at
the email address shown in the beginning of this file. Qianqian will review
the changes and commit it to the subversion if they are satisfactory.
We appreciate any suggestions and feedbacks from you. Please use iso2mesh's
mailing list to report any questions you may have with JSONLab:
http://groups.google.com/group/iso2mesh-users?hl=en&pli=1
(Subscription to the mailing list is needed in order to post messages).

View File

@@ -0,0 +1,32 @@
function val=jsonopt(key,default,varargin)
%
% val=jsonopt(key,default,optstruct)
%
% setting options based on a struct. The struct can be produced
% by varargin2struct from a list of 'param','value' pairs
%
% authors:Qianqian Fang (fangq<at> nmr.mgh.harvard.edu)
%
% $Id: loadjson.m 371 2012-06-20 12:43:06Z fangq $
%
% input:
% key: a string with which one look up a value from a struct
% default: if the key does not exist, return default
% optstruct: a struct where each sub-field is a key
%
% output:
% val: if key exists, val=optstruct.key; otherwise val=default
%
% license:
% BSD, see LICENSE_BSD.txt files for details
%
% -- this function is part of jsonlab toolbox (http://iso2mesh.sf.net/cgi-bin/index.cgi?jsonlab)
%
val=default;
if(nargin<=2) return; end
opt=varargin{1};
if(isstruct(opt) && isfield(opt,key))
val=getfield(opt,key);
end

View File

@@ -0,0 +1,566 @@
function data = loadjson(fname,varargin)
%
% data=loadjson(fname,opt)
% or
% data=loadjson(fname,'param1',value1,'param2',value2,...)
%
% parse a JSON (JavaScript Object Notation) file or string
%
% authors:Qianqian Fang (fangq<at> nmr.mgh.harvard.edu)
% created on 2011/09/09, including previous works from
%
% Nedialko Krouchev: http://www.mathworks.com/matlabcentral/fileexchange/25713
% created on 2009/11/02
% François Glineur: http://www.mathworks.com/matlabcentral/fileexchange/23393
% created on 2009/03/22
% Joel Feenstra:
% http://www.mathworks.com/matlabcentral/fileexchange/20565
% created on 2008/07/03
%
% $Id: loadjson.m 460 2015-01-03 00:30:45Z fangq $
%
% input:
% fname: input file name, if fname contains "{}" or "[]", fname
% will be interpreted as a JSON string
% opt: a struct to store parsing options, opt can be replaced by
% a list of ('param',value) pairs - the param string is equivallent
% to a field in opt. opt can have the following
% fields (first in [.|.] is the default)
%
% opt.SimplifyCell [0|1]: if set to 1, loadjson will call cell2mat
% for each element of the JSON data, and group
% arrays based on the cell2mat rules.
% opt.FastArrayParser [1|0 or integer]: if set to 1, use a
% speed-optimized array parser when loading an
% array object. The fast array parser may
% collapse block arrays into a single large
% array similar to rules defined in cell2mat; 0 to
% use a legacy parser; if set to a larger-than-1
% value, this option will specify the minimum
% dimension to enable the fast array parser. For
% example, if the input is a 3D array, setting
% FastArrayParser to 1 will return a 3D array;
% setting to 2 will return a cell array of 2D
% arrays; setting to 3 will return to a 2D cell
% array of 1D vectors; setting to 4 will return a
% 3D cell array.
% opt.ShowProgress [0|1]: if set to 1, loadjson displays a progress bar.
%
% output:
% dat: a cell array, where {...} blocks are converted into cell arrays,
% and [...] are converted to arrays
%
% examples:
% dat=loadjson('{"obj":{"string":"value","array":[1,2,3]}}')
% dat=loadjson(['examples' filesep 'example1.json'])
% dat=loadjson(['examples' filesep 'example1.json'],'SimplifyCell',1)
%
% license:
% BSD, see LICENSE_BSD.txt files for details
%
% -- this function is part of JSONLab toolbox (http://iso2mesh.sf.net/cgi-bin/index.cgi?jsonlab)
%
global pos inStr len esc index_esc len_esc isoct arraytoken
if(regexp(fname,'[\{\}\]\[]','once'))
string=fname;
elseif(exist(fname,'file'))
fid = fopen(fname,'rb');
string = fread(fid,inf,'uint8=>char')';
fclose(fid);
else
error('input file does not exist');
end
pos = 1; len = length(string); inStr = string;
isoct=exist('OCTAVE_VERSION','builtin');
arraytoken=find(inStr=='[' | inStr==']' | inStr=='"');
jstr=regexprep(inStr,'\\\\',' ');
escquote=regexp(jstr,'\\"');
arraytoken=sort([arraytoken escquote]);
% String delimiters and escape chars identified to improve speed:
esc = find(inStr=='"' | inStr=='\' ); % comparable to: regexp(inStr, '["\\]');
index_esc = 1; len_esc = length(esc);
opt=varargin2struct(varargin{:});
if(jsonopt('ShowProgress',0,opt)==1)
opt.progressbar_=waitbar(0,'loading ...');
end
jsoncount=1;
while pos <= len
switch(next_char)
case '{'
data{jsoncount} = parse_object(opt);
case '['
data{jsoncount} = parse_array(opt);
otherwise
error_pos('Outer level structure must be an object or an array');
end
jsoncount=jsoncount+1;
end % while
jsoncount=length(data);
if(jsoncount==1 && iscell(data))
data=data{1};
end
if(~isempty(data))
if(isstruct(data)) % data can be a struct array
data=jstruct2array(data);
elseif(iscell(data))
data=jcell2array(data);
end
end
if(isfield(opt,'progressbar_'))
close(opt.progressbar_);
end
%%
function newdata=jcell2array(data)
len=length(data);
newdata=data;
for i=1:len
if(isstruct(data{i}))
newdata{i}=jstruct2array(data{i});
elseif(iscell(data{i}))
newdata{i}=jcell2array(data{i});
end
end
%%-------------------------------------------------------------------------
function newdata=jstruct2array(data)
fn=fieldnames(data);
newdata=data;
len=length(data);
for i=1:length(fn) % depth-first
for j=1:len
if(isstruct(getfield(data(j),fn{i})))
newdata(j)=setfield(newdata(j),fn{i},jstruct2array(getfield(data(j),fn{i})));
end
end
end
if(~isempty(strmatch('x0x5F_ArrayType_',fn)) && ~isempty(strmatch('x0x5F_ArrayData_',fn)))
newdata=cell(len,1);
for j=1:len
ndata=cast(data(j).x0x5F_ArrayData_,data(j).x0x5F_ArrayType_);
iscpx=0;
if(~isempty(strmatch('x0x5F_ArrayIsComplex_',fn)))
if(data(j).x0x5F_ArrayIsComplex_)
iscpx=1;
end
end
if(~isempty(strmatch('x0x5F_ArrayIsSparse_',fn)))
if(data(j).x0x5F_ArrayIsSparse_)
if(~isempty(strmatch('x0x5F_ArraySize_',fn)))
dim=data(j).x0x5F_ArraySize_;
if(iscpx && size(ndata,2)==4-any(dim==1))
ndata(:,end-1)=complex(ndata(:,end-1),ndata(:,end));
end
if isempty(ndata)
% All-zeros sparse
ndata=sparse(dim(1),prod(dim(2:end)));
elseif dim(1)==1
% Sparse row vector
ndata=sparse(1,ndata(:,1),ndata(:,2),dim(1),prod(dim(2:end)));
elseif dim(2)==1
% Sparse column vector
ndata=sparse(ndata(:,1),1,ndata(:,2),dim(1),prod(dim(2:end)));
else
% Generic sparse array.
ndata=sparse(ndata(:,1),ndata(:,2),ndata(:,3),dim(1),prod(dim(2:end)));
end
else
if(iscpx && size(ndata,2)==4)
ndata(:,3)=complex(ndata(:,3),ndata(:,4));
end
ndata=sparse(ndata(:,1),ndata(:,2),ndata(:,3));
end
end
elseif(~isempty(strmatch('x0x5F_ArraySize_',fn)))
if(iscpx && size(ndata,2)==2)
ndata=complex(ndata(:,1),ndata(:,2));
end
ndata=reshape(ndata(:),data(j).x0x5F_ArraySize_);
end
newdata{j}=ndata;
end
if(len==1)
newdata=newdata{1};
end
end
%%-------------------------------------------------------------------------
function object = parse_object(varargin)
parse_char('{');
object = [];
if next_char ~= '}'
while 1
str = parseStr(varargin{:});
if isempty(str)
error_pos('Name of value at position %d cannot be empty');
end
parse_char(':');
val = parse_value(varargin{:});
eval( sprintf( 'object.%s = val;', valid_field(str) ) );
if next_char == '}'
break;
end
parse_char(',');
end
end
parse_char('}');
%%-------------------------------------------------------------------------
function object = parse_array(varargin) % JSON array is written in row-major order
global pos inStr isoct
parse_char('[');
object = cell(0, 1);
dim2=[];
arraydepth=jsonopt('JSONLAB_ArrayDepth_',1,varargin{:});
pbar=jsonopt('progressbar_',-1,varargin{:});
if next_char ~= ']'
if(jsonopt('FastArrayParser',1,varargin{:})>=1 && arraydepth>=jsonopt('FastArrayParser',1,varargin{:}))
[endpos, e1l, e1r, maxlevel]=matching_bracket(inStr,pos);
arraystr=['[' inStr(pos:endpos)];
arraystr=regexprep(arraystr,'"_NaN_"','NaN');
arraystr=regexprep(arraystr,'"([-+]*)_Inf_"','$1Inf');
arraystr(arraystr==sprintf('\n'))=[];
arraystr(arraystr==sprintf('\r'))=[];
%arraystr=regexprep(arraystr,'\s*,',','); % this is slow,sometimes needed
if(~isempty(e1l) && ~isempty(e1r)) % the array is in 2D or higher D
astr=inStr((e1l+1):(e1r-1));
astr=regexprep(astr,'"_NaN_"','NaN');
astr=regexprep(astr,'"([-+]*)_Inf_"','$1Inf');
astr(astr==sprintf('\n'))=[];
astr(astr==sprintf('\r'))=[];
astr(astr==' ')='';
if(isempty(find(astr=='[', 1))) % array is 2D
dim2=length(sscanf(astr,'%f,',[1 inf]));
end
else % array is 1D
astr=arraystr(2:end-1);
astr(astr==' ')='';
[obj, count, errmsg, nextidx]=sscanf(astr,'%f,',[1,inf]);
if(nextidx>=length(astr)-1)
object=obj;
pos=endpos;
parse_char(']');
return;
end
end
if(~isempty(dim2))
astr=arraystr;
astr(astr=='[')='';
astr(astr==']')='';
astr(astr==' ')='';
[obj, count, errmsg, nextidx]=sscanf(astr,'%f,',inf);
if(nextidx>=length(astr)-1)
object=reshape(obj,dim2,numel(obj)/dim2)';
pos=endpos;
parse_char(']');
if(pbar>0)
waitbar(pos/length(inStr),pbar,'loading ...');
end
return;
end
end
arraystr=regexprep(arraystr,'\]\s*,','];');
else
arraystr='[';
end
try
if(isoct && regexp(arraystr,'"','once'))
error('Octave eval can produce empty cells for JSON-like input');
end
object=eval(arraystr);
pos=endpos;
catch
while 1
newopt=varargin2struct(varargin{:},'JSONLAB_ArrayDepth_',arraydepth+1);
val = parse_value(newopt);
object{end+1} = val;
if next_char == ']'
break;
end
parse_char(',');
end
end
end
if(jsonopt('SimplifyCell',0,varargin{:})==1)
try
oldobj=object;
object=cell2mat(object')';
if(iscell(oldobj) && isstruct(object) && numel(object)>1 && jsonopt('SimplifyCellArray',1,varargin{:})==0)
object=oldobj;
elseif(size(object,1)>1 && ndims(object)==2)
object=object';
end
catch
end
end
parse_char(']');
if(pbar>0)
waitbar(pos/length(inStr),pbar,'loading ...');
end
%%-------------------------------------------------------------------------
function parse_char(c)
global pos inStr len
skip_whitespace;
if pos > len || inStr(pos) ~= c
error_pos(sprintf('Expected %c at position %%d', c));
else
pos = pos + 1;
skip_whitespace;
end
%%-------------------------------------------------------------------------
function c = next_char
global pos inStr len
skip_whitespace;
if pos > len
c = [];
else
c = inStr(pos);
end
%%-------------------------------------------------------------------------
function skip_whitespace
global pos inStr len
while pos <= len && isspace(inStr(pos))
pos = pos + 1;
end
%%-------------------------------------------------------------------------
function str = parseStr(varargin)
global pos inStr len esc index_esc len_esc
% len, ns = length(inStr), keyboard
if inStr(pos) ~= '"'
error_pos('String starting with " expected at position %d');
else
pos = pos + 1;
end
str = '';
while pos <= len
while index_esc <= len_esc && esc(index_esc) < pos
index_esc = index_esc + 1;
end
if index_esc > len_esc
str = [str inStr(pos:len)];
pos = len + 1;
break;
else
str = [str inStr(pos:esc(index_esc)-1)];
pos = esc(index_esc);
end
nstr = length(str); switch inStr(pos)
case '"'
pos = pos + 1;
if(~isempty(str))
if(strcmp(str,'_Inf_'))
str=Inf;
elseif(strcmp(str,'-_Inf_'))
str=-Inf;
elseif(strcmp(str,'_NaN_'))
str=NaN;
end
end
return;
case '\'
if pos+1 > len
error_pos('End of file reached right after escape character');
end
pos = pos + 1;
switch inStr(pos)
case {'"' '\' '/'}
str(nstr+1) = inStr(pos);
pos = pos + 1;
case {'b' 'f' 'n' 'r' 't'}
str(nstr+1) = sprintf(['\' inStr(pos)]);
pos = pos + 1;
case 'u'
if pos+4 > len
error_pos('End of file reached in escaped unicode character');
end
str(nstr+(1:6)) = inStr(pos-1:pos+4);
pos = pos + 5;
end
otherwise % should never happen
str(nstr+1) = inStr(pos), keyboard
pos = pos + 1;
end
end
error_pos('End of file while expecting end of inStr');
%%-------------------------------------------------------------------------
function num = parse_number(varargin)
global pos inStr len isoct
currstr=inStr(pos:end);
numstr=0;
if(isoct~=0)
numstr=regexp(currstr,'^\s*-?(?:0|[1-9]\d*)(?:\.\d+)?(?:[eE][+\-]?\d+)?','end');
[num, one] = sscanf(currstr, '%f', 1);
delta=numstr+1;
else
[num, one, err, delta] = sscanf(currstr, '%f', 1);
if ~isempty(err)
error_pos('Error reading number at position %d');
end
end
pos = pos + delta-1;
%%-------------------------------------------------------------------------
function val = parse_value(varargin)
global pos inStr len
true = 1; false = 0;
pbar=jsonopt('progressbar_',-1,varargin{:});
if(pbar>0)
waitbar(pos/len,pbar,'loading ...');
end
switch(inStr(pos))
case '"'
val = parseStr(varargin{:});
return;
case '['
val = parse_array(varargin{:});
return;
case '{'
val = parse_object(varargin{:});
if isstruct(val)
if(~isempty(strmatch('x0x5F_ArrayType_',fieldnames(val), 'exact')))
val=jstruct2array(val);
end
elseif isempty(val)
val = struct;
end
return;
case {'-','0','1','2','3','4','5','6','7','8','9'}
val = parse_number(varargin{:});
return;
case 't'
if pos+3 <= len && strcmpi(inStr(pos:pos+3), 'true')
val = true;
pos = pos + 4;
return;
end
case 'f'
if pos+4 <= len && strcmpi(inStr(pos:pos+4), 'false')
val = false;
pos = pos + 5;
return;
end
case 'n'
if pos+3 <= len && strcmpi(inStr(pos:pos+3), 'null')
val = [];
pos = pos + 4;
return;
end
end
error_pos('Value expected at position %d');
%%-------------------------------------------------------------------------
function error_pos(msg)
global pos inStr len
poShow = max(min([pos-15 pos-1 pos pos+20],len),1);
if poShow(3) == poShow(2)
poShow(3:4) = poShow(2)+[0 -1]; % display nothing after
end
msg = [sprintf(msg, pos) ': ' ...
inStr(poShow(1):poShow(2)) '<error>' inStr(poShow(3):poShow(4)) ];
error( ['JSONparser:invalidFormat: ' msg] );
%%-------------------------------------------------------------------------
function str = valid_field(str)
global isoct
% From MATLAB doc: field names must begin with a letter, which may be
% followed by any combination of letters, digits, and underscores.
% Invalid characters will be converted to underscores, and the prefix
% "x0x[Hex code]_" will be added if the first character is not a letter.
pos=regexp(str,'^[^A-Za-z]','once');
if(~isempty(pos))
if(~isoct)
str=regexprep(str,'^([^A-Za-z])','x0x${sprintf(''%X'',unicode2native($1))}_','once');
else
str=sprintf('x0x%X_%s',char(str(1)),str(2:end));
end
end
if(isempty(regexp(str,'[^0-9A-Za-z_]', 'once' ))) return; end
if(~isoct)
str=regexprep(str,'([^0-9A-Za-z_])','_0x${sprintf(''%X'',unicode2native($1))}_');
else
pos=regexp(str,'[^0-9A-Za-z_]');
if(isempty(pos)) return; end
str0=str;
pos0=[0 pos(:)' length(str)];
str='';
for i=1:length(pos)
str=[str str0(pos0(i)+1:pos(i)-1) sprintf('_0x%X_',str0(pos(i)))];
end
if(pos(end)~=length(str))
str=[str str0(pos0(end-1)+1:pos0(end))];
end
end
%str(~isletter(str) & ~('0' <= str & str <= '9')) = '_';
%%-------------------------------------------------------------------------
function endpos = matching_quote(str,pos)
len=length(str);
while(pos<len)
if(str(pos)=='"')
if(~(pos>1 && str(pos-1)=='\'))
endpos=pos;
return;
end
end
pos=pos+1;
end
error('unmatched quotation mark');
%%-------------------------------------------------------------------------
function [endpos, e1l, e1r, maxlevel] = matching_bracket(str,pos)
global arraytoken
level=1;
maxlevel=level;
endpos=0;
bpos=arraytoken(arraytoken>=pos);
tokens=str(bpos);
len=length(tokens);
pos=1;
e1l=[];
e1r=[];
while(pos<=len)
c=tokens(pos);
if(c==']')
level=level-1;
if(isempty(e1r)) e1r=bpos(pos); end
if(level==0)
endpos=bpos(pos);
return
end
end
if(c=='[')
if(isempty(e1l)) e1l=bpos(pos); end
level=level+1;
maxlevel=max(maxlevel,level);
end
if(c=='"')
pos=matching_quote(tokens,pos+1);
end
pos=pos+1;
end
if(endpos==0)
error('unmatched "]"');
end

View File

@@ -0,0 +1,528 @@
function data = loadubjson(fname,varargin)
%
% data=loadubjson(fname,opt)
% or
% data=loadubjson(fname,'param1',value1,'param2',value2,...)
%
% parse a JSON (JavaScript Object Notation) file or string
%
% authors:Qianqian Fang (fangq<at> nmr.mgh.harvard.edu)
% created on 2013/08/01
%
% $Id: loadubjson.m 460 2015-01-03 00:30:45Z fangq $
%
% input:
% fname: input file name, if fname contains "{}" or "[]", fname
% will be interpreted as a UBJSON string
% opt: a struct to store parsing options, opt can be replaced by
% a list of ('param',value) pairs - the param string is equivallent
% to a field in opt. opt can have the following
% fields (first in [.|.] is the default)
%
% opt.SimplifyCell [0|1]: if set to 1, loadubjson will call cell2mat
% for each element of the JSON data, and group
% arrays based on the cell2mat rules.
% opt.IntEndian [B|L]: specify the endianness of the integer fields
% in the UBJSON input data. B - Big-Endian format for
% integers (as required in the UBJSON specification);
% L - input integer fields are in Little-Endian order.
%
% output:
% dat: a cell array, where {...} blocks are converted into cell arrays,
% and [...] are converted to arrays
%
% examples:
% obj=struct('string','value','array',[1 2 3]);
% ubjdata=saveubjson('obj',obj);
% dat=loadubjson(ubjdata)
% dat=loadubjson(['examples' filesep 'example1.ubj'])
% dat=loadubjson(['examples' filesep 'example1.ubj'],'SimplifyCell',1)
%
% license:
% BSD, see LICENSE_BSD.txt files for details
%
% -- this function is part of JSONLab toolbox (http://iso2mesh.sf.net/cgi-bin/index.cgi?jsonlab)
%
global pos inStr len esc index_esc len_esc isoct arraytoken fileendian systemendian
if(regexp(fname,'[\{\}\]\[]','once'))
string=fname;
elseif(exist(fname,'file'))
fid = fopen(fname,'rb');
string = fread(fid,inf,'uint8=>char')';
fclose(fid);
else
error('input file does not exist');
end
pos = 1; len = length(string); inStr = string;
isoct=exist('OCTAVE_VERSION','builtin');
arraytoken=find(inStr=='[' | inStr==']' | inStr=='"');
jstr=regexprep(inStr,'\\\\',' ');
escquote=regexp(jstr,'\\"');
arraytoken=sort([arraytoken escquote]);
% String delimiters and escape chars identified to improve speed:
esc = find(inStr=='"' | inStr=='\' ); % comparable to: regexp(inStr, '["\\]');
index_esc = 1; len_esc = length(esc);
opt=varargin2struct(varargin{:});
fileendian=upper(jsonopt('IntEndian','B',opt));
[os,maxelem,systemendian]=computer;
jsoncount=1;
while pos <= len
switch(next_char)
case '{'
data{jsoncount} = parse_object(opt);
case '['
data{jsoncount} = parse_array(opt);
otherwise
error_pos('Outer level structure must be an object or an array');
end
jsoncount=jsoncount+1;
end % while
jsoncount=length(data);
if(jsoncount==1 && iscell(data))
data=data{1};
end
if(~isempty(data))
if(isstruct(data)) % data can be a struct array
data=jstruct2array(data);
elseif(iscell(data))
data=jcell2array(data);
end
end
%%
function newdata=parse_collection(id,data,obj)
if(jsoncount>0 && exist('data','var'))
if(~iscell(data))
newdata=cell(1);
newdata{1}=data;
data=newdata;
end
end
%%
function newdata=jcell2array(data)
len=length(data);
newdata=data;
for i=1:len
if(isstruct(data{i}))
newdata{i}=jstruct2array(data{i});
elseif(iscell(data{i}))
newdata{i}=jcell2array(data{i});
end
end
%%-------------------------------------------------------------------------
function newdata=jstruct2array(data)
fn=fieldnames(data);
newdata=data;
len=length(data);
for i=1:length(fn) % depth-first
for j=1:len
if(isstruct(getfield(data(j),fn{i})))
newdata(j)=setfield(newdata(j),fn{i},jstruct2array(getfield(data(j),fn{i})));
end
end
end
if(~isempty(strmatch('x0x5F_ArrayType_',fn)) && ~isempty(strmatch('x0x5F_ArrayData_',fn)))
newdata=cell(len,1);
for j=1:len
ndata=cast(data(j).x0x5F_ArrayData_,data(j).x0x5F_ArrayType_);
iscpx=0;
if(~isempty(strmatch('x0x5F_ArrayIsComplex_',fn)))
if(data(j).x0x5F_ArrayIsComplex_)
iscpx=1;
end
end
if(~isempty(strmatch('x0x5F_ArrayIsSparse_',fn)))
if(data(j).x0x5F_ArrayIsSparse_)
if(~isempty(strmatch('x0x5F_ArraySize_',fn)))
dim=double(data(j).x0x5F_ArraySize_);
if(iscpx && size(ndata,2)==4-any(dim==1))
ndata(:,end-1)=complex(ndata(:,end-1),ndata(:,end));
end
if isempty(ndata)
% All-zeros sparse
ndata=sparse(dim(1),prod(dim(2:end)));
elseif dim(1)==1
% Sparse row vector
ndata=sparse(1,ndata(:,1),ndata(:,2),dim(1),prod(dim(2:end)));
elseif dim(2)==1
% Sparse column vector
ndata=sparse(ndata(:,1),1,ndata(:,2),dim(1),prod(dim(2:end)));
else
% Generic sparse array.
ndata=sparse(ndata(:,1),ndata(:,2),ndata(:,3),dim(1),prod(dim(2:end)));
end
else
if(iscpx && size(ndata,2)==4)
ndata(:,3)=complex(ndata(:,3),ndata(:,4));
end
ndata=sparse(ndata(:,1),ndata(:,2),ndata(:,3));
end
end
elseif(~isempty(strmatch('x0x5F_ArraySize_',fn)))
if(iscpx && size(ndata,2)==2)
ndata=complex(ndata(:,1),ndata(:,2));
end
ndata=reshape(ndata(:),data(j).x0x5F_ArraySize_);
end
newdata{j}=ndata;
end
if(len==1)
newdata=newdata{1};
end
end
%%-------------------------------------------------------------------------
function object = parse_object(varargin)
parse_char('{');
object = [];
type='';
count=-1;
if(next_char == '$')
type=inStr(pos+1); % TODO
pos=pos+2;
end
if(next_char == '#')
pos=pos+1;
count=double(parse_number());
end
if next_char ~= '}'
num=0;
while 1
str = parseStr(varargin{:});
if isempty(str)
error_pos('Name of value at position %d cannot be empty');
end
%parse_char(':');
val = parse_value(varargin{:});
num=num+1;
eval( sprintf( 'object.%s = val;', valid_field(str) ) );
if next_char == '}' || (count>=0 && num>=count)
break;
end
%parse_char(',');
end
end
if(count==-1)
parse_char('}');
end
%%-------------------------------------------------------------------------
function [cid,len]=elem_info(type)
id=strfind('iUIlLdD',type);
dataclass={'int8','uint8','int16','int32','int64','single','double'};
bytelen=[1,1,2,4,8,4,8];
if(id>0)
cid=dataclass{id};
len=bytelen(id);
else
error_pos('unsupported type at position %d');
end
%%-------------------------------------------------------------------------
function [data adv]=parse_block(type,count,varargin)
global pos inStr isoct fileendian systemendian
[cid,len]=elem_info(type);
datastr=inStr(pos:pos+len*count-1);
if(isoct)
newdata=int8(datastr);
else
newdata=uint8(datastr);
end
id=strfind('iUIlLdD',type);
if(id<=5 && fileendian~=systemendian)
newdata=swapbytes(typecast(newdata,cid));
end
data=typecast(newdata,cid);
adv=double(len*count);
%%-------------------------------------------------------------------------
function object = parse_array(varargin) % JSON array is written in row-major order
global pos inStr isoct
parse_char('[');
object = cell(0, 1);
dim=[];
type='';
count=-1;
if(next_char == '$')
type=inStr(pos+1);
pos=pos+2;
end
if(next_char == '#')
pos=pos+1;
if(next_char=='[')
dim=parse_array(varargin{:});
count=prod(double(dim));
else
count=double(parse_number());
end
end
if(~isempty(type))
if(count>=0)
[object adv]=parse_block(type,count,varargin{:});
if(~isempty(dim))
object=reshape(object,dim);
end
pos=pos+adv;
return;
else
endpos=matching_bracket(inStr,pos);
[cid,len]=elem_info(type);
count=(endpos-pos)/len;
[object adv]=parse_block(type,count,varargin{:});
pos=pos+adv;
parse_char(']');
return;
end
end
if next_char ~= ']'
while 1
val = parse_value(varargin{:});
object{end+1} = val;
if next_char == ']'
break;
end
%parse_char(',');
end
end
if(jsonopt('SimplifyCell',0,varargin{:})==1)
try
oldobj=object;
object=cell2mat(object')';
if(iscell(oldobj) && isstruct(object) && numel(object)>1 && jsonopt('SimplifyCellArray',1,varargin{:})==0)
object=oldobj;
elseif(size(object,1)>1 && ndims(object)==2)
object=object';
end
catch
end
end
if(count==-1)
parse_char(']');
end
%%-------------------------------------------------------------------------
function parse_char(c)
global pos inStr len
skip_whitespace;
if pos > len || inStr(pos) ~= c
error_pos(sprintf('Expected %c at position %%d', c));
else
pos = pos + 1;
skip_whitespace;
end
%%-------------------------------------------------------------------------
function c = next_char
global pos inStr len
skip_whitespace;
if pos > len
c = [];
else
c = inStr(pos);
end
%%-------------------------------------------------------------------------
function skip_whitespace
global pos inStr len
while pos <= len && isspace(inStr(pos))
pos = pos + 1;
end
%%-------------------------------------------------------------------------
function str = parseStr(varargin)
global pos inStr esc index_esc len_esc
% len, ns = length(inStr), keyboard
type=inStr(pos);
if type ~= 'S' && type ~= 'C' && type ~= 'H'
error_pos('String starting with S expected at position %d');
else
pos = pos + 1;
end
if(type == 'C')
str=inStr(pos);
pos=pos+1;
return;
end
bytelen=double(parse_number());
if(length(inStr)>=pos+bytelen-1)
str=inStr(pos:pos+bytelen-1);
pos=pos+bytelen;
else
error_pos('End of file while expecting end of inStr');
end
%%-------------------------------------------------------------------------
function num = parse_number(varargin)
global pos inStr len isoct fileendian systemendian
id=strfind('iUIlLdD',inStr(pos));
if(isempty(id))
error_pos('expecting a number at position %d');
end
type={'int8','uint8','int16','int32','int64','single','double'};
bytelen=[1,1,2,4,8,4,8];
datastr=inStr(pos+1:pos+bytelen(id));
if(isoct)
newdata=int8(datastr);
else
newdata=uint8(datastr);
end
if(id<=5 && fileendian~=systemendian)
newdata=swapbytes(typecast(newdata,type{id}));
end
num=typecast(newdata,type{id});
pos = pos + bytelen(id)+1;
%%-------------------------------------------------------------------------
function val = parse_value(varargin)
global pos inStr len
true = 1; false = 0;
switch(inStr(pos))
case {'S','C','H'}
val = parseStr(varargin{:});
return;
case '['
val = parse_array(varargin{:});
return;
case '{'
val = parse_object(varargin{:});
if isstruct(val)
if(~isempty(strmatch('x0x5F_ArrayType_',fieldnames(val), 'exact')))
val=jstruct2array(val);
end
elseif isempty(val)
val = struct;
end
return;
case {'i','U','I','l','L','d','D'}
val = parse_number(varargin{:});
return;
case 'T'
val = true;
pos = pos + 1;
return;
case 'F'
val = false;
pos = pos + 1;
return;
case {'Z','N'}
val = [];
pos = pos + 1;
return;
end
error_pos('Value expected at position %d');
%%-------------------------------------------------------------------------
function error_pos(msg)
global pos inStr len
poShow = max(min([pos-15 pos-1 pos pos+20],len),1);
if poShow(3) == poShow(2)
poShow(3:4) = poShow(2)+[0 -1]; % display nothing after
end
msg = [sprintf(msg, pos) ': ' ...
inStr(poShow(1):poShow(2)) '<error>' inStr(poShow(3):poShow(4)) ];
error( ['JSONparser:invalidFormat: ' msg] );
%%-------------------------------------------------------------------------
function str = valid_field(str)
global isoct
% From MATLAB doc: field names must begin with a letter, which may be
% followed by any combination of letters, digits, and underscores.
% Invalid characters will be converted to underscores, and the prefix
% "x0x[Hex code]_" will be added if the first character is not a letter.
pos=regexp(str,'^[^A-Za-z]','once');
if(~isempty(pos))
if(~isoct)
str=regexprep(str,'^([^A-Za-z])','x0x${sprintf(''%X'',unicode2native($1))}_','once');
else
str=sprintf('x0x%X_%s',char(str(1)),str(2:end));
end
end
if(isempty(regexp(str,'[^0-9A-Za-z_]', 'once' ))) return; end
if(~isoct)
str=regexprep(str,'([^0-9A-Za-z_])','_0x${sprintf(''%X'',unicode2native($1))}_');
else
pos=regexp(str,'[^0-9A-Za-z_]');
if(isempty(pos)) return; end
str0=str;
pos0=[0 pos(:)' length(str)];
str='';
for i=1:length(pos)
str=[str str0(pos0(i)+1:pos(i)-1) sprintf('_0x%X_',str0(pos(i)))];
end
if(pos(end)~=length(str))
str=[str str0(pos0(end-1)+1:pos0(end))];
end
end
%str(~isletter(str) & ~('0' <= str & str <= '9')) = '_';
%%-------------------------------------------------------------------------
function endpos = matching_quote(str,pos)
len=length(str);
while(pos<len)
if(str(pos)=='"')
if(~(pos>1 && str(pos-1)=='\'))
endpos=pos;
return;
end
end
pos=pos+1;
end
error('unmatched quotation mark');
%%-------------------------------------------------------------------------
function [endpos e1l e1r maxlevel] = matching_bracket(str,pos)
global arraytoken
level=1;
maxlevel=level;
endpos=0;
bpos=arraytoken(arraytoken>=pos);
tokens=str(bpos);
len=length(tokens);
pos=1;
e1l=[];
e1r=[];
while(pos<=len)
c=tokens(pos);
if(c==']')
level=level-1;
if(isempty(e1r)) e1r=bpos(pos); end
if(level==0)
endpos=bpos(pos);
return
end
end
if(c=='[')
if(isempty(e1l)) e1l=bpos(pos); end
level=level+1;
maxlevel=max(maxlevel,level);
end
if(c=='"')
pos=matching_quote(tokens,pos+1);
end
pos=pos+1;
end
if(endpos==0)
error('unmatched "]"');
end

View File

@@ -0,0 +1,33 @@
function s=mergestruct(s1,s2)
%
% s=mergestruct(s1,s2)
%
% merge two struct objects into one
%
% authors:Qianqian Fang (fangq<at> nmr.mgh.harvard.edu)
% date: 2012/12/22
%
% input:
% s1,s2: a struct object, s1 and s2 can not be arrays
%
% output:
% s: the merged struct object. fields in s1 and s2 will be combined in s.
%
% license:
% BSD, see LICENSE_BSD.txt files for details
%
% -- this function is part of jsonlab toolbox (http://iso2mesh.sf.net/cgi-bin/index.cgi?jsonlab)
%
if(~isstruct(s1) || ~isstruct(s2))
error('input parameters contain non-struct');
end
if(length(s1)>1 || length(s2)>1)
error('can not merge struct arrays');
end
fn=fieldnames(s2);
s=s1;
for i=1:length(fn)
s=setfield(s,fn{i},getfield(s2,fn{i}));
end

View File

@@ -0,0 +1,475 @@
function json=savejson(rootname,obj,varargin)
%
% json=savejson(rootname,obj,filename)
% or
% json=savejson(rootname,obj,opt)
% json=savejson(rootname,obj,'param1',value1,'param2',value2,...)
%
% convert a MATLAB object (cell, struct or array) into a JSON (JavaScript
% Object Notation) string
%
% author: Qianqian Fang (fangq<at> nmr.mgh.harvard.edu)
% created on 2011/09/09
%
% $Id: savejson.m 460 2015-01-03 00:30:45Z fangq $
%
% input:
% rootname: the name of the root-object, when set to '', the root name
% is ignored, however, when opt.ForceRootName is set to 1 (see below),
% the MATLAB variable name will be used as the root name.
% obj: a MATLAB object (array, cell, cell array, struct, struct array).
% filename: a string for the file name to save the output JSON data.
% opt: a struct for additional options, ignore to use default values.
% opt can have the following fields (first in [.|.] is the default)
%
% opt.FileName [''|string]: a file name to save the output JSON data
% opt.FloatFormat ['%.10g'|string]: format to show each numeric element
% of a 1D/2D array;
% opt.ArrayIndent [1|0]: if 1, output explicit data array with
% precedent indentation; if 0, no indentation
% opt.ArrayToStruct[0|1]: when set to 0, savejson outputs 1D/2D
% array in JSON array format; if sets to 1, an
% array will be shown as a struct with fields
% "_ArrayType_", "_ArraySize_" and "_ArrayData_"; for
% sparse arrays, the non-zero elements will be
% saved to _ArrayData_ field in triplet-format i.e.
% (ix,iy,val) and "_ArrayIsSparse_" will be added
% with a value of 1; for a complex array, the
% _ArrayData_ array will include two columns
% (4 for sparse) to record the real and imaginary
% parts, and also "_ArrayIsComplex_":1 is added.
% opt.ParseLogical [0|1]: if this is set to 1, logical array elem
% will use true/false rather than 1/0.
% opt.NoRowBracket [1|0]: if this is set to 1, arrays with a single
% numerical element will be shown without a square
% bracket, unless it is the root object; if 0, square
% brackets are forced for any numerical arrays.
% opt.ForceRootName [0|1]: when set to 1 and rootname is empty, savejson
% will use the name of the passed obj variable as the
% root object name; if obj is an expression and
% does not have a name, 'root' will be used; if this
% is set to 0 and rootname is empty, the root level
% will be merged down to the lower level.
% opt.Inf ['"$1_Inf_"'|string]: a customized regular expression pattern
% to represent +/-Inf. The matched pattern is '([-+]*)Inf'
% and $1 represents the sign. For those who want to use
% 1e999 to represent Inf, they can set opt.Inf to '$11e999'
% opt.NaN ['"_NaN_"'|string]: a customized regular expression pattern
% to represent NaN
% opt.JSONP [''|string]: to generate a JSONP output (JSON with padding),
% for example, if opt.JSONP='foo', the JSON data is
% wrapped inside a function call as 'foo(...);'
% opt.UnpackHex [1|0]: conver the 0x[hex code] output by loadjson
% back to the string form
% opt.SaveBinary [0|1]: 1 - save the JSON file in binary mode; 0 - text mode.
% opt.Compact [0|1]: 1- out compact JSON format (remove all newlines and tabs)
%
% opt can be replaced by a list of ('param',value) pairs. The param
% string is equivallent to a field in opt and is case sensitive.
% output:
% json: a string in the JSON format (see http://json.org)
%
% examples:
% jsonmesh=struct('MeshNode',[0 0 0;1 0 0;0 1 0;1 1 0;0 0 1;1 0 1;0 1 1;1 1 1],...
% 'MeshTetra',[1 2 4 8;1 3 4 8;1 2 6 8;1 5 6 8;1 5 7 8;1 3 7 8],...
% 'MeshTri',[1 2 4;1 2 6;1 3 4;1 3 7;1 5 6;1 5 7;...
% 2 8 4;2 8 6;3 8 4;3 8 7;5 8 6;5 8 7],...
% 'MeshCreator','FangQ','MeshTitle','T6 Cube',...
% 'SpecialData',[nan, inf, -inf]);
% savejson('jmesh',jsonmesh)
% savejson('',jsonmesh,'ArrayIndent',0,'FloatFormat','\t%.5g')
%
% license:
% BSD, see LICENSE_BSD.txt files for details
%
% -- this function is part of JSONLab toolbox (http://iso2mesh.sf.net/cgi-bin/index.cgi?jsonlab)
%
if(nargin==1)
varname=inputname(1);
obj=rootname;
if(isempty(varname))
varname='root';
end
rootname=varname;
else
varname=inputname(2);
end
if(length(varargin)==1 && ischar(varargin{1}))
opt=struct('FileName',varargin{1});
else
opt=varargin2struct(varargin{:});
end
opt.IsOctave=exist('OCTAVE_VERSION','builtin');
rootisarray=0;
rootlevel=1;
forceroot=jsonopt('ForceRootName',0,opt);
if((isnumeric(obj) || islogical(obj) || ischar(obj) || isstruct(obj) || iscell(obj)) && isempty(rootname) && forceroot==0)
rootisarray=1;
rootlevel=0;
else
if(isempty(rootname))
rootname=varname;
end
end
if((isstruct(obj) || iscell(obj))&& isempty(rootname) && forceroot)
rootname='root';
end
whitespaces=struct('tab',sprintf('\t'),'newline',sprintf('\n'),'sep',sprintf(',\n'));
if(jsonopt('Compact',0,opt)==1)
whitespaces=struct('tab','','newline','','sep',',');
end
if(~isfield(opt,'whitespaces_'))
opt.whitespaces_=whitespaces;
end
nl=whitespaces.newline;
json=obj2json(rootname,obj,rootlevel,opt);
if(rootisarray)
json=sprintf('%s%s',json,nl);
else
json=sprintf('{%s%s%s}\n',nl,json,nl);
end
jsonp=jsonopt('JSONP','',opt);
if(~isempty(jsonp))
json=sprintf('%s(%s);%s',jsonp,json,nl);
end
% save to a file if FileName is set, suggested by Patrick Rapin
if(~isempty(jsonopt('FileName','',opt)))
if(jsonopt('SaveBinary',0,opt)==1)
fid = fopen(opt.FileName, 'wb');
fwrite(fid,json);
else
fid = fopen(opt.FileName, 'wt');
fwrite(fid,json,'char');
end
fclose(fid);
end
%%-------------------------------------------------------------------------
function txt=obj2json(name,item,level,varargin)
if(iscell(item))
txt=cell2json(name,item,level,varargin{:});
elseif(isstruct(item))
txt=struct2json(name,item,level,varargin{:});
elseif(ischar(item))
txt=str2json(name,item,level,varargin{:});
else
txt=mat2json(name,item,level,varargin{:});
end
%%-------------------------------------------------------------------------
function txt=cell2json(name,item,level,varargin)
txt='';
if(~iscell(item))
error('input is not a cell');
end
dim=size(item);
if(ndims(squeeze(item))>2) % for 3D or higher dimensions, flatten to 2D for now
item=reshape(item,dim(1),numel(item)/dim(1));
dim=size(item);
end
len=numel(item);
ws=jsonopt('whitespaces_',struct('tab',sprintf('\t'),'newline',sprintf('\n'),'sep',sprintf(',\n')),varargin{:});
padding0=repmat(ws.tab,1,level);
padding2=repmat(ws.tab,1,level+1);
nl=ws.newline;
if(len>1)
if(~isempty(name))
txt=sprintf('%s"%s": [%s',padding0, checkname(name,varargin{:}),nl); name='';
else
txt=sprintf('%s[%s',padding0,nl);
end
elseif(len==0)
if(~isempty(name))
txt=sprintf('%s"%s": []',padding0, checkname(name,varargin{:})); name='';
else
txt=sprintf('%s[]',padding0);
end
end
for j=1:dim(2)
if(dim(1)>1) txt=sprintf('%s%s[%s',txt,padding2,nl); end
for i=1:dim(1)
txt=sprintf('%s%s',txt,obj2json(name,item{i,j},level+(dim(1)>1)+1,varargin{:}));
if(i<dim(1)) txt=sprintf('%s%s',txt,sprintf(',%s',nl)); end
end
if(dim(1)>1) txt=sprintf('%s%s%s]',txt,nl,padding2); end
if(j<dim(2)) txt=sprintf('%s%s',txt,sprintf(',%s',nl)); end
%if(j==dim(2)) txt=sprintf('%s%s',txt,sprintf(',%s',nl)); end
end
if(len>1) txt=sprintf('%s%s%s]',txt,nl,padding0); end
%%-------------------------------------------------------------------------
function txt=struct2json(name,item,level,varargin)
txt='';
if(~isstruct(item))
error('input is not a struct');
end
dim=size(item);
if(ndims(squeeze(item))>2) % for 3D or higher dimensions, flatten to 2D for now
item=reshape(item,dim(1),numel(item)/dim(1));
dim=size(item);
end
len=numel(item);
ws=struct('tab',sprintf('\t'),'newline',sprintf('\n'));
ws=jsonopt('whitespaces_',ws,varargin{:});
padding0=repmat(ws.tab,1,level);
padding2=repmat(ws.tab,1,level+1);
padding1=repmat(ws.tab,1,level+(dim(1)>1)+(len>1));
nl=ws.newline;
if(~isempty(name))
if(len>1) txt=sprintf('%s"%s": [%s',padding0,checkname(name,varargin{:}),nl); end
else
if(len>1) txt=sprintf('%s[%s',padding0,nl); end
end
for j=1:dim(2)
if(dim(1)>1) txt=sprintf('%s%s[%s',txt,padding2,nl); end
for i=1:dim(1)
names = fieldnames(item(i,j));
if(~isempty(name) && len==1)
txt=sprintf('%s%s"%s": {%s',txt,padding1, checkname(name,varargin{:}),nl);
else
txt=sprintf('%s%s{%s',txt,padding1,nl);
end
if(~isempty(names))
for e=1:length(names)
txt=sprintf('%s%s',txt,obj2json(names{e},getfield(item(i,j),...
names{e}),level+(dim(1)>1)+1+(len>1),varargin{:}));
if(e<length(names)) txt=sprintf('%s%s',txt,','); end
txt=sprintf('%s%s',txt,nl);
end
end
txt=sprintf('%s%s}',txt,padding1);
if(i<dim(1)) txt=sprintf('%s%s',txt,sprintf(',%s',nl)); end
end
if(dim(1)>1) txt=sprintf('%s%s%s]',txt,nl,padding2); end
if(j<dim(2)) txt=sprintf('%s%s',txt,sprintf(',%s',nl)); end
end
if(len>1) txt=sprintf('%s%s%s]',txt,nl,padding0); end
%%-------------------------------------------------------------------------
function txt=str2json(name,item,level,varargin)
txt='';
if(~ischar(item))
error('input is not a string');
end
item=reshape(item, max(size(item),[1 0]));
len=size(item,1);
ws=struct('tab',sprintf('\t'),'newline',sprintf('\n'),'sep',sprintf(',\n'));
ws=jsonopt('whitespaces_',ws,varargin{:});
padding1=repmat(ws.tab,1,level);
padding0=repmat(ws.tab,1,level+1);
nl=ws.newline;
sep=ws.sep;
if(~isempty(name))
if(len>1) txt=sprintf('%s"%s": [%s',padding1,checkname(name,varargin{:}),nl); end
else
if(len>1) txt=sprintf('%s[%s',padding1,nl); end
end
isoct=jsonopt('IsOctave',0,varargin{:});
for e=1:len
if(isoct)
val=regexprep(item(e,:),'\\','\\');
val=regexprep(val,'"','\"');
val=regexprep(val,'^"','\"');
else
val=regexprep(item(e,:),'\\','\\\\');
val=regexprep(val,'"','\\"');
val=regexprep(val,'^"','\\"');
end
val=escapejsonstring(val);
if(len==1)
obj=['"' checkname(name,varargin{:}) '": ' '"',val,'"'];
if(isempty(name)) obj=['"',val,'"']; end
txt=sprintf('%s%s%s%s',txt,padding1,obj);
else
txt=sprintf('%s%s%s%s',txt,padding0,['"',val,'"']);
end
if(e==len) sep=''; end
txt=sprintf('%s%s',txt,sep);
end
if(len>1) txt=sprintf('%s%s%s%s',txt,nl,padding1,']'); end
%%-------------------------------------------------------------------------
function txt=mat2json(name,item,level,varargin)
if(~isnumeric(item) && ~islogical(item))
error('input is not an array');
end
ws=struct('tab',sprintf('\t'),'newline',sprintf('\n'),'sep',sprintf(',\n'));
ws=jsonopt('whitespaces_',ws,varargin{:});
padding1=repmat(ws.tab,1,level);
padding0=repmat(ws.tab,1,level+1);
nl=ws.newline;
sep=ws.sep;
if(length(size(item))>2 || issparse(item) || ~isreal(item) || ...
isempty(item) ||jsonopt('ArrayToStruct',0,varargin{:}))
if(isempty(name))
txt=sprintf('%s{%s%s"_ArrayType_": "%s",%s%s"_ArraySize_": %s,%s',...
padding1,nl,padding0,class(item),nl,padding0,regexprep(mat2str(size(item)),'\s+',','),nl);
else
txt=sprintf('%s"%s": {%s%s"_ArrayType_": "%s",%s%s"_ArraySize_": %s,%s',...
padding1,checkname(name,varargin{:}),nl,padding0,class(item),nl,padding0,regexprep(mat2str(size(item)),'\s+',','),nl);
end
else
if(numel(item)==1 && jsonopt('NoRowBracket',1,varargin{:})==1 && level>0)
numtxt=regexprep(regexprep(matdata2json(item,level+1,varargin{:}),'^\[',''),']','');
else
numtxt=matdata2json(item,level+1,varargin{:});
end
if(isempty(name))
txt=sprintf('%s%s',padding1,numtxt);
else
if(numel(item)==1 && jsonopt('NoRowBracket',1,varargin{:})==1)
txt=sprintf('%s"%s": %s',padding1,checkname(name,varargin{:}),numtxt);
else
txt=sprintf('%s"%s": %s',padding1,checkname(name,varargin{:}),numtxt);
end
end
return;
end
dataformat='%s%s%s%s%s';
if(issparse(item))
[ix,iy]=find(item);
data=full(item(find(item)));
if(~isreal(item))
data=[real(data(:)),imag(data(:))];
if(size(item,1)==1)
% Kludge to have data's 'transposedness' match item's.
% (Necessary for complex row vector handling below.)
data=data';
end
txt=sprintf(dataformat,txt,padding0,'"_ArrayIsComplex_": ','1', sep);
end
txt=sprintf(dataformat,txt,padding0,'"_ArrayIsSparse_": ','1', sep);
if(size(item,1)==1)
% Row vector, store only column indices.
txt=sprintf(dataformat,txt,padding0,'"_ArrayData_": ',...
matdata2json([iy(:),data'],level+2,varargin{:}), nl);
elseif(size(item,2)==1)
% Column vector, store only row indices.
txt=sprintf(dataformat,txt,padding0,'"_ArrayData_": ',...
matdata2json([ix,data],level+2,varargin{:}), nl);
else
% General case, store row and column indices.
txt=sprintf(dataformat,txt,padding0,'"_ArrayData_": ',...
matdata2json([ix,iy,data],level+2,varargin{:}), nl);
end
else
if(isreal(item))
txt=sprintf(dataformat,txt,padding0,'"_ArrayData_": ',...
matdata2json(item(:)',level+2,varargin{:}), nl);
else
txt=sprintf(dataformat,txt,padding0,'"_ArrayIsComplex_": ','1', sep);
txt=sprintf(dataformat,txt,padding0,'"_ArrayData_": ',...
matdata2json([real(item(:)) imag(item(:))],level+2,varargin{:}), nl);
end
end
txt=sprintf('%s%s%s',txt,padding1,'}');
%%-------------------------------------------------------------------------
function txt=matdata2json(mat,level,varargin)
ws=struct('tab',sprintf('\t'),'newline',sprintf('\n'),'sep',sprintf(',\n'));
ws=jsonopt('whitespaces_',ws,varargin{:});
tab=ws.tab;
nl=ws.newline;
if(size(mat,1)==1)
pre='';
post='';
level=level-1;
else
pre=sprintf('[%s',nl);
post=sprintf('%s%s]',nl,repmat(tab,1,level-1));
end
if(isempty(mat))
txt='null';
return;
end
floatformat=jsonopt('FloatFormat','%.10g',varargin{:});
%if(numel(mat)>1)
formatstr=['[' repmat([floatformat ','],1,size(mat,2)-1) [floatformat sprintf('],%s',nl)]];
%else
% formatstr=[repmat([floatformat ','],1,size(mat,2)-1) [floatformat sprintf(',\n')]];
%end
if(nargin>=2 && size(mat,1)>1 && jsonopt('ArrayIndent',1,varargin{:})==1)
formatstr=[repmat(tab,1,level) formatstr];
end
txt=sprintf(formatstr,mat');
txt(end-length(nl):end)=[];
if(islogical(mat) && jsonopt('ParseLogical',0,varargin{:})==1)
txt=regexprep(txt,'1','true');
txt=regexprep(txt,'0','false');
end
%txt=regexprep(mat2str(mat),'\s+',',');
%txt=regexprep(txt,';',sprintf('],\n['));
% if(nargin>=2 && size(mat,1)>1)
% txt=regexprep(txt,'\[',[repmat(sprintf('\t'),1,level) '[']);
% end
txt=[pre txt post];
if(any(isinf(mat(:))))
txt=regexprep(txt,'([-+]*)Inf',jsonopt('Inf','"$1_Inf_"',varargin{:}));
end
if(any(isnan(mat(:))))
txt=regexprep(txt,'NaN',jsonopt('NaN','"_NaN_"',varargin{:}));
end
%%-------------------------------------------------------------------------
function newname=checkname(name,varargin)
isunpack=jsonopt('UnpackHex',1,varargin{:});
newname=name;
if(isempty(regexp(name,'0x([0-9a-fA-F]+)_','once')))
return
end
if(isunpack)
isoct=jsonopt('IsOctave',0,varargin{:});
if(~isoct)
newname=regexprep(name,'(^x|_){1}0x([0-9a-fA-F]+)_','${native2unicode(hex2dec($2))}');
else
pos=regexp(name,'(^x|_){1}0x([0-9a-fA-F]+)_','start');
pend=regexp(name,'(^x|_){1}0x([0-9a-fA-F]+)_','end');
if(isempty(pos)) return; end
str0=name;
pos0=[0 pend(:)' length(name)];
newname='';
for i=1:length(pos)
newname=[newname str0(pos0(i)+1:pos(i)-1) char(hex2dec(str0(pos(i)+3:pend(i)-1)))];
end
if(pos(end)~=length(name))
newname=[newname str0(pos0(end-1)+1:pos0(end))];
end
end
end
%%-------------------------------------------------------------------------
function newstr=escapejsonstring(str)
newstr=str;
isoct=exist('OCTAVE_VERSION','builtin');
if(isoct)
vv=sscanf(OCTAVE_VERSION,'%f');
if(vv(1)>=3.8) isoct=0; end
end
if(isoct)
escapechars={'\a','\f','\n','\r','\t','\v'};
for i=1:length(escapechars);
newstr=regexprep(newstr,escapechars{i},escapechars{i});
end
else
escapechars={'\a','\b','\f','\n','\r','\t','\v'};
for i=1:length(escapechars);
newstr=regexprep(newstr,escapechars{i},regexprep(escapechars{i},'\\','\\\\'));
end
end

View File

@@ -0,0 +1,504 @@
function json=saveubjson(rootname,obj,varargin)
%
% json=saveubjson(rootname,obj,filename)
% or
% json=saveubjson(rootname,obj,opt)
% json=saveubjson(rootname,obj,'param1',value1,'param2',value2,...)
%
% convert a MATLAB object (cell, struct or array) into a Universal
% Binary JSON (UBJSON) binary string
%
% author: Qianqian Fang (fangq<at> nmr.mgh.harvard.edu)
% created on 2013/08/17
%
% $Id: saveubjson.m 460 2015-01-03 00:30:45Z fangq $
%
% input:
% rootname: the name of the root-object, when set to '', the root name
% is ignored, however, when opt.ForceRootName is set to 1 (see below),
% the MATLAB variable name will be used as the root name.
% obj: a MATLAB object (array, cell, cell array, struct, struct array)
% filename: a string for the file name to save the output UBJSON data
% opt: a struct for additional options, ignore to use default values.
% opt can have the following fields (first in [.|.] is the default)
%
% opt.FileName [''|string]: a file name to save the output JSON data
% opt.ArrayToStruct[0|1]: when set to 0, saveubjson outputs 1D/2D
% array in JSON array format; if sets to 1, an
% array will be shown as a struct with fields
% "_ArrayType_", "_ArraySize_" and "_ArrayData_"; for
% sparse arrays, the non-zero elements will be
% saved to _ArrayData_ field in triplet-format i.e.
% (ix,iy,val) and "_ArrayIsSparse_" will be added
% with a value of 1; for a complex array, the
% _ArrayData_ array will include two columns
% (4 for sparse) to record the real and imaginary
% parts, and also "_ArrayIsComplex_":1 is added.
% opt.ParseLogical [1|0]: if this is set to 1, logical array elem
% will use true/false rather than 1/0.
% opt.NoRowBracket [1|0]: if this is set to 1, arrays with a single
% numerical element will be shown without a square
% bracket, unless it is the root object; if 0, square
% brackets are forced for any numerical arrays.
% opt.ForceRootName [0|1]: when set to 1 and rootname is empty, saveubjson
% will use the name of the passed obj variable as the
% root object name; if obj is an expression and
% does not have a name, 'root' will be used; if this
% is set to 0 and rootname is empty, the root level
% will be merged down to the lower level.
% opt.JSONP [''|string]: to generate a JSONP output (JSON with padding),
% for example, if opt.JSON='foo', the JSON data is
% wrapped inside a function call as 'foo(...);'
% opt.UnpackHex [1|0]: conver the 0x[hex code] output by loadjson
% back to the string form
%
% opt can be replaced by a list of ('param',value) pairs. The param
% string is equivallent to a field in opt and is case sensitive.
% output:
% json: a binary string in the UBJSON format (see http://ubjson.org)
%
% examples:
% jsonmesh=struct('MeshNode',[0 0 0;1 0 0;0 1 0;1 1 0;0 0 1;1 0 1;0 1 1;1 1 1],...
% 'MeshTetra',[1 2 4 8;1 3 4 8;1 2 6 8;1 5 6 8;1 5 7 8;1 3 7 8],...
% 'MeshTri',[1 2 4;1 2 6;1 3 4;1 3 7;1 5 6;1 5 7;...
% 2 8 4;2 8 6;3 8 4;3 8 7;5 8 6;5 8 7],...
% 'MeshCreator','FangQ','MeshTitle','T6 Cube',...
% 'SpecialData',[nan, inf, -inf]);
% saveubjson('jsonmesh',jsonmesh)
% saveubjson('jsonmesh',jsonmesh,'meshdata.ubj')
%
% license:
% BSD, see LICENSE_BSD.txt files for details
%
% -- this function is part of JSONLab toolbox (http://iso2mesh.sf.net/cgi-bin/index.cgi?jsonlab)
%
if(nargin==1)
varname=inputname(1);
obj=rootname;
if(isempty(varname))
varname='root';
end
rootname=varname;
else
varname=inputname(2);
end
if(length(varargin)==1 && ischar(varargin{1}))
opt=struct('FileName',varargin{1});
else
opt=varargin2struct(varargin{:});
end
opt.IsOctave=exist('OCTAVE_VERSION','builtin');
rootisarray=0;
rootlevel=1;
forceroot=jsonopt('ForceRootName',0,opt);
if((isnumeric(obj) || islogical(obj) || ischar(obj) || isstruct(obj) || iscell(obj)) && isempty(rootname) && forceroot==0)
rootisarray=1;
rootlevel=0;
else
if(isempty(rootname))
rootname=varname;
end
end
if((isstruct(obj) || iscell(obj))&& isempty(rootname) && forceroot)
rootname='root';
end
json=obj2ubjson(rootname,obj,rootlevel,opt);
if(~rootisarray)
json=['{' json '}'];
end
jsonp=jsonopt('JSONP','',opt);
if(~isempty(jsonp))
json=[jsonp '(' json ')'];
end
% save to a file if FileName is set, suggested by Patrick Rapin
if(~isempty(jsonopt('FileName','',opt)))
fid = fopen(opt.FileName, 'wb');
fwrite(fid,json);
fclose(fid);
end
%%-------------------------------------------------------------------------
function txt=obj2ubjson(name,item,level,varargin)
if(iscell(item))
txt=cell2ubjson(name,item,level,varargin{:});
elseif(isstruct(item))
txt=struct2ubjson(name,item,level,varargin{:});
elseif(ischar(item))
txt=str2ubjson(name,item,level,varargin{:});
else
txt=mat2ubjson(name,item,level,varargin{:});
end
%%-------------------------------------------------------------------------
function txt=cell2ubjson(name,item,level,varargin)
txt='';
if(~iscell(item))
error('input is not a cell');
end
dim=size(item);
if(ndims(squeeze(item))>2) % for 3D or higher dimensions, flatten to 2D for now
item=reshape(item,dim(1),numel(item)/dim(1));
dim=size(item);
end
len=numel(item); % let's handle 1D cell first
if(len>1)
if(~isempty(name))
txt=[S_(checkname(name,varargin{:})) '[']; name='';
else
txt='[';
end
elseif(len==0)
if(~isempty(name))
txt=[S_(checkname(name,varargin{:})) 'Z']; name='';
else
txt='Z';
end
end
for j=1:dim(2)
if(dim(1)>1) txt=[txt '[']; end
for i=1:dim(1)
txt=[txt obj2ubjson(name,item{i,j},level+(len>1),varargin{:})];
end
if(dim(1)>1) txt=[txt ']']; end
end
if(len>1) txt=[txt ']']; end
%%-------------------------------------------------------------------------
function txt=struct2ubjson(name,item,level,varargin)
txt='';
if(~isstruct(item))
error('input is not a struct');
end
dim=size(item);
if(ndims(squeeze(item))>2) % for 3D or higher dimensions, flatten to 2D for now
item=reshape(item,dim(1),numel(item)/dim(1));
dim=size(item);
end
len=numel(item);
if(~isempty(name))
if(len>1) txt=[S_(checkname(name,varargin{:})) '[']; end
else
if(len>1) txt='['; end
end
for j=1:dim(2)
if(dim(1)>1) txt=[txt '[']; end
for i=1:dim(1)
names = fieldnames(item(i,j));
if(~isempty(name) && len==1)
txt=[txt S_(checkname(name,varargin{:})) '{'];
else
txt=[txt '{'];
end
if(~isempty(names))
for e=1:length(names)
txt=[txt obj2ubjson(names{e},getfield(item(i,j),...
names{e}),level+(dim(1)>1)+1+(len>1),varargin{:})];
end
end
txt=[txt '}'];
end
if(dim(1)>1) txt=[txt ']']; end
end
if(len>1) txt=[txt ']']; end
%%-------------------------------------------------------------------------
function txt=str2ubjson(name,item,level,varargin)
txt='';
if(~ischar(item))
error('input is not a string');
end
item=reshape(item, max(size(item),[1 0]));
len=size(item,1);
if(~isempty(name))
if(len>1) txt=[S_(checkname(name,varargin{:})) '[']; end
else
if(len>1) txt='['; end
end
isoct=jsonopt('IsOctave',0,varargin{:});
for e=1:len
val=item(e,:);
if(len==1)
obj=['' S_(checkname(name,varargin{:})) '' '',S_(val),''];
if(isempty(name)) obj=['',S_(val),'']; end
txt=[txt,'',obj];
else
txt=[txt,'',['',S_(val),'']];
end
end
if(len>1) txt=[txt ']']; end
%%-------------------------------------------------------------------------
function txt=mat2ubjson(name,item,level,varargin)
if(~isnumeric(item) && ~islogical(item))
error('input is not an array');
end
if(length(size(item))>2 || issparse(item) || ~isreal(item) || ...
isempty(item) || jsonopt('ArrayToStruct',0,varargin{:}))
cid=I_(uint32(max(size(item))));
if(isempty(name))
txt=['{' S_('_ArrayType_'),S_(class(item)),S_('_ArraySize_'),I_a(size(item),cid(1)) ];
else
if(isempty(item))
txt=[S_(checkname(name,varargin{:})),'Z'];
return;
else
txt=[S_(checkname(name,varargin{:})),'{',S_('_ArrayType_'),S_(class(item)),S_('_ArraySize_'),I_a(size(item),cid(1))];
end
end
else
if(isempty(name))
txt=matdata2ubjson(item,level+1,varargin{:});
else
if(numel(item)==1 && jsonopt('NoRowBracket',1,varargin{:})==1)
numtxt=regexprep(regexprep(matdata2ubjson(item,level+1,varargin{:}),'^\[',''),']','');
txt=[S_(checkname(name,varargin{:})) numtxt];
else
txt=[S_(checkname(name,varargin{:})),matdata2ubjson(item,level+1,varargin{:})];
end
end
return;
end
if(issparse(item))
[ix,iy]=find(item);
data=full(item(find(item)));
if(~isreal(item))
data=[real(data(:)),imag(data(:))];
if(size(item,1)==1)
% Kludge to have data's 'transposedness' match item's.
% (Necessary for complex row vector handling below.)
data=data';
end
txt=[txt,S_('_ArrayIsComplex_'),'T'];
end
txt=[txt,S_('_ArrayIsSparse_'),'T'];
if(size(item,1)==1)
% Row vector, store only column indices.
txt=[txt,S_('_ArrayData_'),...
matdata2ubjson([iy(:),data'],level+2,varargin{:})];
elseif(size(item,2)==1)
% Column vector, store only row indices.
txt=[txt,S_('_ArrayData_'),...
matdata2ubjson([ix,data],level+2,varargin{:})];
else
% General case, store row and column indices.
txt=[txt,S_('_ArrayData_'),...
matdata2ubjson([ix,iy,data],level+2,varargin{:})];
end
else
if(isreal(item))
txt=[txt,S_('_ArrayData_'),...
matdata2ubjson(item(:)',level+2,varargin{:})];
else
txt=[txt,S_('_ArrayIsComplex_'),'T'];
txt=[txt,S_('_ArrayData_'),...
matdata2ubjson([real(item(:)) imag(item(:))],level+2,varargin{:})];
end
end
txt=[txt,'}'];
%%-------------------------------------------------------------------------
function txt=matdata2ubjson(mat,level,varargin)
if(isempty(mat))
txt='Z';
return;
end
if(size(mat,1)==1)
level=level-1;
end
type='';
hasnegtive=(mat<0);
if(isa(mat,'integer') || isinteger(mat) || (isfloat(mat) && all(mod(mat(:),1) == 0)))
if(isempty(hasnegtive))
if(max(mat(:))<=2^8)
type='U';
end
end
if(isempty(type))
% todo - need to consider negative ones separately
id= histc(abs(max(mat(:))),[0 2^7 2^15 2^31 2^63]);
if(isempty(find(id)))
error('high-precision data is not yet supported');
end
key='iIlL';
type=key(find(id));
end
txt=[I_a(mat(:),type,size(mat))];
elseif(islogical(mat))
logicalval='FT';
if(numel(mat)==1)
txt=logicalval(mat+1);
else
txt=['[$U#' I_a(size(mat),'l') typecast(swapbytes(uint8(mat(:)')),'uint8')];
end
else
if(numel(mat)==1)
txt=['[' D_(mat) ']'];
else
txt=D_a(mat(:),'D',size(mat));
end
end
%txt=regexprep(mat2str(mat),'\s+',',');
%txt=regexprep(txt,';',sprintf('],['));
% if(nargin>=2 && size(mat,1)>1)
% txt=regexprep(txt,'\[',[repmat(sprintf('\t'),1,level) '[']);
% end
if(any(isinf(mat(:))))
txt=regexprep(txt,'([-+]*)Inf',jsonopt('Inf','"$1_Inf_"',varargin{:}));
end
if(any(isnan(mat(:))))
txt=regexprep(txt,'NaN',jsonopt('NaN','"_NaN_"',varargin{:}));
end
%%-------------------------------------------------------------------------
function newname=checkname(name,varargin)
isunpack=jsonopt('UnpackHex',1,varargin{:});
newname=name;
if(isempty(regexp(name,'0x([0-9a-fA-F]+)_','once')))
return
end
if(isunpack)
isoct=jsonopt('IsOctave',0,varargin{:});
if(~isoct)
newname=regexprep(name,'(^x|_){1}0x([0-9a-fA-F]+)_','${native2unicode(hex2dec($2))}');
else
pos=regexp(name,'(^x|_){1}0x([0-9a-fA-F]+)_','start');
pend=regexp(name,'(^x|_){1}0x([0-9a-fA-F]+)_','end');
if(isempty(pos)) return; end
str0=name;
pos0=[0 pend(:)' length(name)];
newname='';
for i=1:length(pos)
newname=[newname str0(pos0(i)+1:pos(i)-1) char(hex2dec(str0(pos(i)+3:pend(i)-1)))];
end
if(pos(end)~=length(name))
newname=[newname str0(pos0(end-1)+1:pos0(end))];
end
end
end
%%-------------------------------------------------------------------------
function val=S_(str)
if(length(str)==1)
val=['C' str];
else
val=['S' I_(int32(length(str))) str];
end
%%-------------------------------------------------------------------------
function val=I_(num)
if(~isinteger(num))
error('input is not an integer');
end
if(num>=0 && num<255)
val=['U' data2byte(swapbytes(cast(num,'uint8')),'uint8')];
return;
end
key='iIlL';
cid={'int8','int16','int32','int64'};
for i=1:4
if((num>0 && num<2^(i*8-1)) || (num<0 && num>=-2^(i*8-1)))
val=[key(i) data2byte(swapbytes(cast(num,cid{i})),'uint8')];
return;
end
end
error('unsupported integer');
%%-------------------------------------------------------------------------
function val=D_(num)
if(~isfloat(num))
error('input is not a float');
end
if(isa(num,'single'))
val=['d' data2byte(num,'uint8')];
else
val=['D' data2byte(num,'uint8')];
end
%%-------------------------------------------------------------------------
function data=I_a(num,type,dim,format)
id=find(ismember('iUIlL',type));
if(id==0)
error('unsupported integer array');
end
% based on UBJSON specs, all integer types are stored in big endian format
if(id==1)
data=data2byte(swapbytes(int8(num)),'uint8');
blen=1;
elseif(id==2)
data=data2byte(swapbytes(uint8(num)),'uint8');
blen=1;
elseif(id==3)
data=data2byte(swapbytes(int16(num)),'uint8');
blen=2;
elseif(id==4)
data=data2byte(swapbytes(int32(num)),'uint8');
blen=4;
elseif(id==5)
data=data2byte(swapbytes(int64(num)),'uint8');
blen=8;
end
if(nargin>=3 && length(dim)>=2 && prod(dim)~=dim(2))
format='opt';
end
if((nargin<4 || strcmp(format,'opt')) && numel(num)>1)
if(nargin>=3 && (length(dim)==1 || (length(dim)>=2 && prod(dim)~=dim(2))))
cid=I_(uint32(max(dim)));
data=['$' type '#' I_a(dim,cid(1)) data(:)'];
else
data=['$' type '#' I_(int32(numel(data)/blen)) data(:)'];
end
data=['[' data(:)'];
else
data=reshape(data,blen,numel(data)/blen);
data(2:blen+1,:)=data;
data(1,:)=type;
data=data(:)';
data=['[' data(:)' ']'];
end
%%-------------------------------------------------------------------------
function data=D_a(num,type,dim,format)
id=find(ismember('dD',type));
if(id==0)
error('unsupported float array');
end
if(id==1)
data=data2byte(single(num),'uint8');
elseif(id==2)
data=data2byte(double(num),'uint8');
end
if(nargin>=3 && length(dim)>=2 && prod(dim)~=dim(2))
format='opt';
end
if((nargin<4 || strcmp(format,'opt')) && numel(num)>1)
if(nargin>=3 && (length(dim)==1 || (length(dim)>=2 && prod(dim)~=dim(2))))
cid=I_(uint32(max(dim)));
data=['$' type '#' I_a(dim,cid(1)) data(:)'];
else
data=['$' type '#' I_(int32(numel(data)/(id*4))) data(:)'];
end
data=['[' data];
else
data=reshape(data,(id*4),length(data)/(id*4));
data(2:(id*4+1),:)=data;
data(1,:)=type;
data=data(:)';
data=['[' data(:)' ']'];
end
%%-------------------------------------------------------------------------
function bytes=data2byte(varargin)
bytes=typecast(varargin{:});
bytes=bytes(:)';

View File

@@ -0,0 +1,40 @@
function opt=varargin2struct(varargin)
%
% opt=varargin2struct('param1',value1,'param2',value2,...)
% or
% opt=varargin2struct(...,optstruct,...)
%
% convert a series of input parameters into a structure
%
% authors:Qianqian Fang (fangq<at> nmr.mgh.harvard.edu)
% date: 2012/12/22
%
% input:
% 'param', value: the input parameters should be pairs of a string and a value
% optstruct: if a parameter is a struct, the fields will be merged to the output struct
%
% output:
% opt: a struct where opt.param1=value1, opt.param2=value2 ...
%
% license:
% BSD, see LICENSE_BSD.txt files for details
%
% -- this function is part of jsonlab toolbox (http://iso2mesh.sf.net/cgi-bin/index.cgi?jsonlab)
%
len=length(varargin);
opt=struct;
if(len==0) return; end
i=1;
while(i<=len)
if(isstruct(varargin{i}))
opt=mergestruct(opt,varargin{i});
elseif(ischar(varargin{i}) && i<len)
opt=setfield(opt,varargin{i},varargin{i+1});
i=i+1;
else
error('input must be in the form of ...,''name'',value,... pairs or structs');
end
i=i+1;
end

View File

@@ -0,0 +1,30 @@
function str = makeValidFieldName(str)
% From MATLAB doc: field names must begin with a letter, which may be
% followed by any combination of letters, digits, and underscores.
% Invalid characters will be converted to underscores, and the prefix
% "x0x[Hex code]_" will be added if the first character is not a letter.
isoct=exist('OCTAVE_VERSION','builtin');
pos=regexp(str,'^[^A-Za-z]','once');
if(~isempty(pos))
if(~isoct)
str=regexprep(str,'^([^A-Za-z])','x0x${sprintf(''%X'',unicode2native($1))}_','once');
else
str=sprintf('x0x%X_%s',char(str(1)),str(2:end));
end
end
if(isempty(regexp(str,'[^0-9A-Za-z_]', 'once' ))) return; end
if(~isoct)
str=regexprep(str,'([^0-9A-Za-z_])','_0x${sprintf(''%X'',unicode2native($1))}_');
else
pos=regexp(str,'[^0-9A-Za-z_]');
if(isempty(pos)) return; end
str0=str;
pos0=[0 pos(:)' length(str)];
str='';
for i=1:length(pos)
str=[str str0(pos0(i)+1:pos(i)-1) sprintf('_0x%X_',str0(pos(i)))];
end
if(pos(end)~=length(str))
str=[str str0(pos0(end-1)+1:pos0(end))];
end
end

View File

@@ -0,0 +1,125 @@
function submitWithConfiguration(conf)
addpath('./lib/jsonlab');
parts = parts(conf);
fprintf('== Submitting solutions | %s...\n', conf.itemName);
tokenFile = 'token.mat';
if exist(tokenFile, 'file')
load(tokenFile);
[email token] = promptToken(email, token, tokenFile);
else
[email token] = promptToken('', '', tokenFile);
end
if isempty(token)
fprintf('!! Submission Cancelled\n');
return
end
try
response = submitParts(conf, email, token, parts);
catch
e = lasterror();
fprintf( ...
'!! Submission failed: unexpected error: %s\n', ...
e.message);
fprintf('!! Please try again later.\n');
return
end
if isfield(response, 'errorMessage')
fprintf('!! Submission failed: %s\n', response.errorMessage);
else
showFeedback(parts, response);
save(tokenFile, 'email', 'token');
end
end
function [email token] = promptToken(email, existingToken, tokenFile)
if (~isempty(email) && ~isempty(existingToken))
prompt = sprintf( ...
'Use token from last successful submission (%s)? (Y/n): ', ...
email);
reenter = input(prompt, 's');
if (isempty(reenter) || reenter(1) == 'Y' || reenter(1) == 'y')
token = existingToken;
return;
else
delete(tokenFile);
end
end
email = input('Login (email address): ', 's');
token = input('Token: ', 's');
end
function isValid = isValidPartOptionIndex(partOptions, i)
isValid = (~isempty(i)) && (1 <= i) && (i <= numel(partOptions));
end
function response = submitParts(conf, email, token, parts)
body = makePostBody(conf, email, token, parts);
submissionUrl = submissionUrl();
params = {'jsonBody', body};
responseBody = urlread(submissionUrl, 'post', params);
response = loadjson(responseBody);
end
function body = makePostBody(conf, email, token, parts)
bodyStruct.assignmentSlug = conf.assignmentSlug;
bodyStruct.submitterEmail = email;
bodyStruct.secret = token;
bodyStruct.parts = makePartsStruct(conf, parts);
opt.Compact = 1;
body = savejson('', bodyStruct, opt);
end
function partsStruct = makePartsStruct(conf, parts)
for part = parts
partId = part{:}.id;
fieldName = makeValidFieldName(partId);
outputStruct.output = conf.output(partId);
partsStruct.(fieldName) = outputStruct;
end
end
function [parts] = parts(conf)
parts = {};
for partArray = conf.partArrays
part.id = partArray{:}{1};
part.sourceFiles = partArray{:}{2};
part.name = partArray{:}{3};
parts{end + 1} = part;
end
end
function showFeedback(parts, response)
fprintf('== \n');
fprintf('== %43s | %9s | %-s\n', 'Part Name', 'Score', 'Feedback');
fprintf('== %43s | %9s | %-s\n', '---------', '-----', '--------');
for part = parts
score = '';
partFeedback = '';
partFeedback = response.partFeedbacks.(makeValidFieldName(part{:}.id));
partEvaluation = response.partEvaluations.(makeValidFieldName(part{:}.id));
score = sprintf('%d / %3d', partEvaluation.score, partEvaluation.maxScore);
fprintf('== %43s | %9s | %-s\n', part{:}.name, score, partFeedback);
end
evaluation = response.evaluation;
totalScore = sprintf('%d / %d', evaluation.score, evaluation.maxScore);
fprintf('== --------------------------------\n');
fprintf('== %43s | %9s | %-s\n', '', totalScore, '');
fprintf('== \n');
end
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% Service configuration
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function submissionUrl = submissionUrl()
submissionUrl = 'https://www-origin.coursera.org/api/onDemandProgrammingImmediateFormSubmissions.v1';
end

View File

@@ -0,0 +1,25 @@
function movieList = loadMovieList()
%GETMOVIELIST reads the fixed movie list in movie.txt and returns a
%cell array of the words
% movieList = GETMOVIELIST() reads the fixed movie list in movie.txt
% and returns a cell array of the words in movieList.
%% Read the fixed movieulary list
fid = fopen('movie_ids.txt');
% Store all movies in cell array movie{}
n = 1682; % Total number of movies
movieList = cell(n, 1);
for i = 1:n
% Read line
line = fgets(fid);
% Word Index (can ignore since it will be = i)
[idx, movieName] = strtok(line, ' ');
% Actual Word
movieList{i} = strtrim(movieName);
end
fclose(fid);
end

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,22 @@
function p = multivariateGaussian(X, mu, Sigma2)
%MULTIVARIATEGAUSSIAN Computes the probability density function of the
%multivariate gaussian distribution.
% p = MULTIVARIATEGAUSSIAN(X, mu, Sigma2) Computes the probability
% density function of the examples X under the multivariate gaussian
% distribution with parameters mu and Sigma2. If Sigma2 is a matrix, it is
% treated as the covariance matrix. If Sigma2 is a vector, it is treated
% as the \sigma^2 values of the variances in each dimension (a diagonal
% covariance matrix)
%
k = length(mu);
if (size(Sigma2, 2) == 1) || (size(Sigma2, 1) == 1)
Sigma2 = diag(Sigma2);
end
X = bsxfun(@minus, X, mu(:)');
p = (2 * pi) ^ (- k / 2) * det(Sigma2) ^ (-0.5) * ...
exp(-0.5 * sum(bsxfun(@times, X * pinv(Sigma2), X), 2));
end

View File

@@ -0,0 +1,17 @@
function [Ynorm, Ymean] = normalizeRatings(Y, R)
%NORMALIZERATINGS Preprocess data by subtracting mean rating for every
%movie (every row)
% [Ynorm, Ymean] = NORMALIZERATINGS(Y, R) normalized Y so that each movie
% has a rating of 0 on average, and returns the mean rating in Ymean.
%
[m, n] = size(Y);
Ymean = zeros(m, 1);
Ynorm = zeros(size(Y));
for i = 1:m
idx = find(R(i, :) == 1);
Ymean(i) = mean(Y(i, idx));
Ynorm(i, idx) = Y(i, idx) - Ymean(i);
end
end

View File

@@ -0,0 +1,46 @@
function [bestEpsilon bestF1] = selectThreshold(yval, pval)
%SELECTTHRESHOLD Find the best threshold (epsilon) to use for selecting
%outliers
% [bestEpsilon bestF1] = SELECTTHRESHOLD(yval, pval) finds the best
% threshold to use for selecting outliers based on the results from a
% validation set (pval) and the ground truth (yval).
%
bestEpsilon = 0;
bestF1 = 0;
F1 = 0;
stepsize = (max(pval) - min(pval)) / 1000;
for epsilon = min(pval):stepsize:max(pval)
% ====================== YOUR CODE HERE ======================
% Instructions: Compute the F1 score of choosing epsilon as the
% threshold and place the value in F1. The code at the
% end of the loop will compare the F1 score for this
% choice of epsilon and set it to be the best epsilon if
% it is better than the current choice of epsilon.
%
% Note: You can use predictions = (pval < epsilon) to get a binary vector
% of 0's and 1's of the outlier predictions
% =============================================================
if F1 > bestF1
bestF1 = F1;
bestEpsilon = epsilon;
end
end
end

View File

@@ -0,0 +1,76 @@
function submit()
addpath('./lib');
conf.assignmentSlug = 'anomaly-detection-and-recommender-systems';
conf.itemName = 'Anomaly Detection and Recommender Systems';
conf.partArrays = { ...
{ ...
'1', ...
{ 'estimateGaussian.m' }, ...
'Estimate Gaussian Parameters', ...
}, ...
{ ...
'2', ...
{ 'selectThreshold.m' }, ...
'Select Threshold', ...
}, ...
{ ...
'3', ...
{ 'cofiCostFunc.m' }, ...
'Collaborative Filtering Cost', ...
}, ...
{ ...
'4', ...
{ 'cofiCostFunc.m' }, ...
'Collaborative Filtering Gradient', ...
}, ...
{ ...
'5', ...
{ 'cofiCostFunc.m' }, ...
'Regularized Cost', ...
}, ...
{ ...
'6', ...
{ 'cofiCostFunc.m' }, ...
'Regularized Gradient', ...
}, ...
};
conf.output = @output;
submitWithConfiguration(conf);
end
function out = output(partId, auxstring)
% Random Test Cases
n_u = 3; n_m = 4; n = 5;
X = reshape(sin(1:n_m*n), n_m, n);
Theta = reshape(cos(1:n_u*n), n_u, n);
Y = reshape(sin(1:2:2*n_m*n_u), n_m, n_u);
R = Y > 0.5;
pval = [abs(Y(:)) ; 0.001; 1];
yval = [R(:) ; 1; 0];
params = [X(:); Theta(:)];
if partId == '1'
[mu sigma2] = estimateGaussian(X);
out = sprintf('%0.5f ', [mu(:); sigma2(:)]);
elseif partId == '2'
[bestEpsilon bestF1] = selectThreshold(yval, pval);
out = sprintf('%0.5f ', [bestEpsilon(:); bestF1(:)]);
elseif partId == '3'
[J] = cofiCostFunc(params, Y, R, n_u, n_m, ...
n, 0);
out = sprintf('%0.5f ', J(:));
elseif partId == '4'
[J, grad] = cofiCostFunc(params, Y, R, n_u, n_m, ...
n, 0);
out = sprintf('%0.5f ', grad(:));
elseif partId == '5'
[J] = cofiCostFunc(params, Y, R, n_u, n_m, ...
n, 1.5);
out = sprintf('%0.5f ', J(:));
elseif partId == '6'
[J, grad] = cofiCostFunc(params, Y, R, n_u, n_m, ...
n, 1.5);
out = sprintf('%0.5f ', grad(:));
end
end

View File

@@ -0,0 +1,20 @@
function visualizeFit(X, mu, sigma2)
%VISUALIZEFIT Visualize the dataset and its estimated distribution.
% VISUALIZEFIT(X, p, mu, sigma2) This visualization shows you the
% probability density function of the Gaussian distribution. Each example
% has a location (x1, x2) that depends on its feature values.
%
[X1,X2] = meshgrid(0:.5:35);
Z = multivariateGaussian([X1(:) X2(:)],mu,sigma2);
Z = reshape(Z,size(X1));
plot(X(:, 1), X(:, 2),'bx');
hold on;
% Do not plot if there are infinities
if (sum(isinf(Z)) == 0)
contour(X1, X2, Z, 10.^(-20:3:0)');
end
hold off;
end