function varargout = FitChiTool(varargin)
% Please type web('<FitChiToolDir>\html\FitChiToolHelp.html') or open FitChiToolHelp in an HTML
% browser for information on the FitChiTool GUI.
%
% Author: N. Brahms
% Created: 2-28-06
% Last updated: 2-28-06
% Version: 1.0
% Required files:
% FitChiTool.m - This file
% FitChiTool.fig - GUIDE figure for drawing FitChiTool
% defaultChiSquareModels.mat - Fitting library
% fitChiSquare.m - The chi-square fitting routine. This can be found in
% its latest release on the Matlab File Exchange.
% java/FitChiToolModel.class - Class to implement the data model for the
% guess and result tables.
% Auxiliary files:
% html/FitChiToolHelp.html - Web help, accessed from the Help menu
% html/images/* - Files for web help
% ModelEditor.m - Code for the model editor
% ModelEditor.fig - GUIDE figure for the model editor
% fitChiToolExample.m - Example use file for FitChiTool.
%
% Version history
% 1.0 - Initial release
%
% Known issues:
% 1. JTable throws an exception when fitChiSquare returns NaN for the
% dchi2=1 bounds. Fixing this requires implementing a new cell editor
% for the bounds columns.
%
% Last Modified by GUIDE v2.5 02-Mar-2006 14:34:09
%====================================================================
% Procedure
%====================================================================
% Set up the java path
warning off
basepath = which('FitChiTool.m');
javaaddpath([basepath(1:findstr(basepath,'FitChiTool.m')-1) 'java']);
warning on
% Import javax stuff
import javax.swing.*;
import javax.swing.table.*;
import ca.odell.renderpack.*;
% Begin initialization code - DO NOT EDIT
gui_Singleton = 0;
gui_State = struct('gui_Name', mfilename, ...
'gui_Singleton', gui_Singleton, ...
'gui_OpeningFcn', @FitChiTool_OpeningFcn, ...
'gui_OutputFcn', @FitChiTool_OutputFcn, ...
'gui_LayoutFcn', [] , ...
'gui_Callback', []);
if nargin && ischar(varargin{1})
gui_State.gui_Callback = str2func(varargin{1});
end
if nargout
[varargout{1:nargout}] = gui_mainfcn(gui_State, varargin{:});
else
gui_mainfcn(gui_State, varargin{:});
end
% End initialization code - DO NOT EDIT
%=================================================================
% Creation Functions
%=================================================================
% --- Executes just before FitChiTool is made visible.
function FitChiTool_OpeningFcn(hObject, eventdata, handles, varargin)
% Initialize flages
handles.dataFittable = 0;
handles.dataPlottable = 0;
handles.errorsGood = 0;
% Choose default command line output for FitChiTool
handles.output= hObject;
% JAVA OBJECTS:
% Add the Java Swing table objects
[handles.guessscroll,handles.guesstable] = makeParamTable('guess',[],@guesstable_Callback);
[handles.resultscroll,handles.resulttable] = makeParamTable('result',[],@resulttable_Callback);
% Prevent user editing of the results object
% handles.resulttable.setEnabled(0);
javacomponent(handles.guessscroll,[20 100 250 90],hObject);
javacomponent(handles.resultscroll,[300 20 250 90],hObject);
% handles.guessscroll.getColumnHeader().setReorderingAllowed(java.lang.Boolean.FALSE);
% handles.resultscroll.getColumnHeader().setReorderingAllowed(java.lang.Boolean.FALSE);
loadbutton_Callback(handles.loadbutton,[],handles);
% Load library models
DEFAULT_LIBRARY = 'defaultChiSquareModels.mat';
handles.library = DEFAULT_LIBRARY;
handles = loadModels(handles);
% Update handles structure
guidata(hObject, handles);
% ---- Default object creation function
function hObjectOut = defaultCreateFcn(hObjectIn)
hObjectOut = hObjectIn;
if ispc && isequal(get(hObjectIn,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
set(hObjectOut,'BackgroundColor','white');
end
%==================================================================
% Output Function
%===================================================================
% --- Outputs from this function are returned to the command line.
function varargout = FitChiTool_OutputFcn(hObject, eventdata, handles)
% Get default command line output from handles structure
varargout{1} = handles.output;
%===================================================================
% Objects
%===================================================================
% --- guess/results table ------------------------------------------
%
% Acknowledgement: Brad Phelan (certain parts of this function copyright
% Brad Phelan 2005). http://xtargets.com
function [scroller,table] = makeParamTable(mode,data,callbackFcn)
import javax.swing.*;
import javax.swing.table.*;
center = 0; lower = -Inf; upper = Inf; uncFit = true;
% Create table data and column names
[namevec, data] = fillTable(mode,center,lower,upper,uncFit);
% Create table
switch mode
case 'result'
model = FitChiToolModel(data,namevec,0);
otherwise
model = FitChiToolModel(data,namevec);
end
table = JTable(model);
table.setAutoResizeMode(JTable.AUTO_RESIZE_LAST_COLUMN);
%Fix for JTable focus bug
table.putClientProperty('terminateEditOnFocusLost',java.lang.Boolean.TRUE);
table.getTableHeader().setReorderingAllowed(false);
set(table.getModel(),'TableChangedCallback',callbackFcn);
% Create scrollbar
scroller = JScrollPane(table);
scroller.setVerticalScrollBarPolicy(...
ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED);
% --- resultstable callback ----------------------------------------
function resulttable_Callback(source, event)
% --- resultstable callback ----------------------------------------
function guesstable_Callback(source, event)
% --- xdatamenu ----------------------------------------------------
function xdatamenu_ButtonDown(hObject, eventdata, handles)
function xdatamenu_Callback(hObject, eventdata, handles)
handles = conditionData(handles);
dataPlot(handles);
guidata(hObject,handles);
function xdatamenu_CreateFcn(hObject, eventdata, handles)
hObject = defaultCreateFcn(hObject);
% --- dxdatamenu -------------------------------------------------
function dxdatamenu_Callback(hObject, eventdata, handles)
guidata(hObject,conditionData(handles));
function dxdatamenu_CreateFcn(hObject, eventdata, handles)
hObject = defaultCreateFcn(hObject);
% --- ydatamenu ----------------------------------------------------
function ydatamenu_Callback(hObject, eventdata, handles)
handles = conditionData(handles);
dataPlot(handles);
guidata(hObject,handles);
function ydatamenu_CreateFcn(hObject, eventdata, handles)
hObject = defaultCreateFcn(hObject);
% --- dydatamenu --------------------------------------------------
function dydatamenu_Callback(hObject, eventdata, handles)
guidata(hObject,conditionData(handles));
function dydatamenu_CreateFcn(hObject, eventdata, handles)
hObject = defaultCreateFcn(hObject);
% --- groupmenu ----------------------------------------------------
function groupmenu_Callback(hObject, eventdata, handles)
i = get(hObject,'Value')-1;
if i>0
children = handles.models(i).children;
set(handles.modelmenu,'String',['<select a model>' {children.name}]);
else
set(handles.modelmenu,'String','<select a group>');
end
set(handles.modelmenu,'Value',1);
set(handles.descriptionedit,'String','');
guidata(hObject,handles);
function groupmenu_CreateFcn(hObject, eventdata, handles)
hObject = defaultCreateFcn(hObject);
% --- modelmenu -------------------------------------------------
function modelmenu_Callback(hObject, eventdata, handles)
i = get(handles.groupmenu,'Value')-1;
j = get(handles.modelmenu,'Value')-1;
if i && j
set(handles.descriptionedit,'String',...
handles.models(i).children(j).desc);
else
set(handles.descriptionedit,'String','');
end
handles = setFitEnable(setGuess(handles));
guidata(hObject,handles);
function modelmenu_CreateFcn(hObject, eventdata, handles)
hObject = defaultCreateFcn(hObject);
% --- guessedit ------------------------------------------------
function guessedit_Callback(hObject, eventdata, handles)
function guessedit_CreateFcn(hObject, eventdata, handles)
hObject = defaultCreateFcn(hObject);
% --- checkboxerrors ------------------------------------------
function checkboxerrors_Callback(hObject, eventdata, handles)
guidata(hObject,setFitEnable(handles));
% --- fitbutton -----------------------------------------------
function fitbutton_Callback(hObject, eventdata, handles)
% Disable the figure so no one does anything now
allhandles = [handles.xdatamenu handles.dxdatamenu handles.ydatamenu...
handles.dydatamenu handles.loadbutton handles.checkboxerrors...
handles.fitbutton handles.modelmenu handles.groupmenu];
menuhandles = [handles.savemenu handles.saveasmenu handles.workspacemenu];
set(allhandles,'Enable','off');
guidata(handles.figure1,handles);
drawnow
try
% Get the data from the guess table
guessVec = handles.guesstable.getModel().getDataVector();
for i=1:guessVec.size()
guessRowVec = guessVec.get(i-1);
for j=1:guessRowVec.size()
if ischar(guessRowVec.get(j-1))
data(i,j)=str2num(guessRowVec.get(j-1));
else
data(i,j)=guessRowVec.get(j-1);
end
end
end
% The guess
guess = data(:,3)';
% Fitting options
op = optimset('Display','off');
op.ErrorsUnknown = get(handles.checkboxerrors,'Value');
op.LowerBound = data(:,2)';
op.UpperBound = data(:,4)';
op.FitUncertainty = data(:,5)';
% Model
i = get(handles.groupmenu,'Value')-1;
j = get(handles.modelmenu,'Value')-1;
model = eval(handles.models(i).children(j).model);
% Perform the fit
warning off
[fit.params, fit.dParams, fit.gof, fit.stddev] = fitChiSquare(handles.xdata,...
handles.ydata,model,guess,...
handles.dxdata,handles.dydata,op);
warning on
% Get the fit
fit.expected = feval(model,fit.params,handles.xdata);
% Plot the fit results
axes(handles.fitplot)
cla reset
errorbar(handles.xdata(:,1),handles.ydata,fit.stddev,'.');
hold on
plot(handles.xdata(:,1),fit.expected,'-r');
% Plot the residuals
axes(handles.residualplot)
cla reset
plot(handles.xdata(:,1),(fit.expected-handles.ydata)./fit.stddev,'.');
hold on
plot(handles.xdata(:,1),zeros(length(handles.ydata),1),'-r');
% Output results
clear data;
center = fit.params; lower = fit.params-[fit.dParams.dl];...
upper = fit.params+[fit.dParams.du]; uncFit = op.FitUncertainty;
[names, data] = fillTable('result',center,lower,upper,uncFit);
handles.resulttable.getModel().setDataVector(data,names);
handles.fit = fit;
set(handles.rchi2edit,'String',num2str(fit.gof.chi2/fit.gof.dof));
set(handles.dofedit,'String',num2str(fit.gof.dof));
set(handles.eta2edit,'String',num2str(fit.gof.eta2));
set(menuhandles,'Enable','on');
catch
s = lasterror;
names = {s.stack.name};
stackstr = [s.message sprintf('\nStack trace:\n')];
for j=1:length(names)
stackstr = [stackstr names{j} sprintf('\n')];
end
errordlg(stackstr);
end
% Turn the figure back on
set(allhandles,'Enable','on');
guidata(handles.figure1,handles);
% --- fitbutton enable ----------------------------------------
function handles = setFitEnable(handlesIn)
handles=handlesIn;
if ~get(handles.checkboxerrors,'Value') && ~handles.errorsGood || ...
~handles.dataFittable || get(handles.modelmenu,'Value')==1
set(handles.fitbutton,'Enable','off');
else
set(handles.fitbutton,'Enable','on');
end
% --- loadbutton ----------------------------------------------
function loadbutton_Callback(hObject, eventdata, handles)
% Get workspace variable names
names = evalin('base','who');
varData=[];
varNames=[];
j=1;
for i=1:length(names)
% Save workspace variable here if it is numeric
if evalin('base',['isnumeric(' names{i} ')'])
varData{j} = evalin('base',names{i});
varNames{j} = names{i};
j=j+1;
end
end
% Load the menu boxes
menus = [handles.xdatamenu handles.dxdatamenu ...
handles.ydatamenu handles.dydatamenu];
if ~isempty(varNames)
set(menus,'String',['<choose a variable>' varNames]);
set(menus,'Value',1);
else
set(menus,'String',{'<load variables from workspace>'});
set(menus,'Value',1);
end
axes(handles.fitplot); cla; axes(handles.residualplot); cla;
set(handles.fitbutton,'Enable','off');
guidata(hObject,handles);
% ------ model description edit --------------------------------------
function descriptionedit_Callback(hObject, eventdata, handles)
function descriptionedit_CreateFcn(hObject, eventdata, handles)
if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
set(hObject,'BackgroundColor','white');
end
% ------ reduced chi-square edit -------------------------------------
function rchi2edit_Callback(hObject, eventdata, handles)
function rchi2edit_CreateFcn(hObject, eventdata, handles)
if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
set(hObject,'BackgroundColor','white');
end
% ------ dof edit ----------------------------------------------------
function dofedit_Callback(hObject, eventdata, handles)
function dofedit_CreateFcn(hObject, eventdata, handles)
if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
set(hObject,'BackgroundColor','white');
end
% ------ eta2 edit ---------------------------------------------------
function eta2edit_Callback(hObject, eventdata, handles)
function eta2edit_CreateFcn(hObject, eventdata, handles)
if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
set(hObject,'BackgroundColor','white');
end
%===================================================================
% Menus
%===================================================================
% --------------------------------------------------------------------
function filemenu_Callback(hObject, eventdata, handles)
% --------------------------------------------------------------------
function librarymenu_Callback(hObject, eventdata, handles)
[file, path] = uigetfile('*.mat','Choose a library file...');
% If a file was chosen
if any(file~=0)
try
% Load the file
warning off
load([path file],'-mat','models');
warning on
% Do some error checking
wellformed = 1;
if(isstruct(models))
for i=1:length(models)
if(isstruct(models(i).children) && ischar(models(i).group))
for j=1:length(models(i).children)
if ischar(models(i).children(j).name)
wellformed = wellformed && 1;
else
wellformed = 0;
end
end
else
wellformed = 0;
end
end
else
wellformed = 0;
end
if wellformed
handles.models = models;
handles.library = [path file];
handles = loadModels(handles);
else
errordlg(['File ' path file ' format error.']);
end
catch
errordlg(['File ' path file ' contains no models.']);
end
end
guidata(hObject,handles);
% --------------------------------------------------------------------
function importmenu_Callback(hObject, eventdata, handles)
msgbox(['Feature coming soon. Use the "Load variables" button to '...
'load variables from the MATLAB workspace.'],'','help');
% --------------------------------------------------------------------
function savemenu_Callback(hObject, eventdata, handles)
% If a file name exists, save using that path. Otherwise, save as...
if isfield(handles,'path') && exist(handles.path,'file')
savefit(handles);
else
saveasmenu_Callback(handles.saveasmenu,eventdata,handles);
end
% --------------------------------------------------------------------
function saveasmenu_Callback(hObject, eventdata, handles)
[file, path] = uiputfile('*.mat');
if any(file~=0)
handles.path = path;
handles.file = file;
savefit(handles)
end
guidata(hObject,handles);
function savefit(handles)
if isfield(handles,'fit') && ~isempty(handles.fit) &&...
exist(handles.path,'file') && any(handles.file~=0)
fit = handles.fit;
save([handles.path handles.file],'fit');
end
% --------------------------------------------------------------------
function workspacemenu_Callback(hObject, eventdata, handles)
if (isfield(handles,'fit') && ~isempty(handles.fit))
answer = inputdlg({'Save fit to workspace in a sruct named:'},'',1,...
{'fit'});
else
errordlg('Please perform a fit first.');
end
if(ischar(answer{1}))
try
assignin('base',answer{1},handles.fit);
catch
s = lasterror;
errordlg(s.message);
end
end
% --------------------------------------------------------------------
function modeleditmenu_Callback(hObject, eventdata, handles)
ModelEditor(handles.library);
% --------------------------------------------------------------------
function fitoptionsmenu_Callback(hObject, eventdata, handles)
msgbox(['Feature coming soon. Use the command-line interface to '...
'fitChiSquare to access additional options'],'','help');
% --------------------------------------------------------------------
function menutools_Callback(hObject, eventdata, handles)
% --------------------------------------------------------------------
function helpmenu_Callback(hObject, eventdata, handles)
% --------------------------------------------------------------------
function helpdisplaymenu_Callback(hObject, eventdata, handles)
path = which('FitChiTool.m');
web([path(1:findstr(path,'FitChiTool.m')-1) 'html\FitChiToolHelp.html']);
%===================================================================
% Called functions
%===================================================================
% ---- Guess length -------------------------------------------------
function handles = setGuess(handlesIn)
handles = handlesIn;
i = get(handles.groupmenu,'Value')-1;
j = get(handles.modelmenu,'Value')-1;
if(get(handles.modelmenu,'Value')==1 | get(handles.xdatamenu,'Value')==1)
center = 0; lower = -Inf; upper = Inf; uncFit = true;
else
lengthfcn = eval(handles.models(i).children(j).length);
numrows=feval(lengthfcn,handles.xdata);
center = zeros(1,numrows); lower = -Inf*ones(1,numrows);...
upper = Inf*ones(1,numrows); uncFit = ones(1,numrows);
end
[namevec, data] = fillTable('guess',center,lower,upper,uncFit);
handles.guesstable.getModel().setDataVector(data,namevec);
guidata(handles.figure1,handles);
% ---- Table filling ------------------------------------------------
function [names,data] = fillTable(mode,center,lower,upper,uncFit)
switch mode
case 'guess'
names = {'#' 'Lower Bound' 'Guess' 'Upper Bound' 'Unc. Fit?'};
case 'result'
names = {'#' 'Lower dChi2=1' 'Parameter' 'Upper dChi2=1' 'Unc. Fit?'};
otherwise
names = {' ' ' ' ' ' ' ' ' '};
end
for k=1:length(center)
data{k,1} = java.lang.Integer(k);
data{k,2} = lower(k);
data{k,3} = center(k);
data{k,4} = upper(k);
if uncFit(k)
data{k,5} = java.lang.Boolean.TRUE;
else
data{k,5} = java.lang.Boolean.FALSE;
end
end
% ---- Model loading ------------------------------------------------
function handles = loadModels(handlesIn)
handles = handlesIn;
load(handles.library);
handles.models = models;
set(handles.groupmenu,'String',['<select a group>' {models.group}]);
set(handles.groupmenu,'Value',1);
set(handles.modelmenu,'String','<select a group>');
set(handles.modelmenu,'Value',1);
% ---- Plotting -----------------------------------------------------
% Used to plot the data when a new data vector is entered
% Also clears any fitting results
function dataPlot(handles)
handles.fit = []; handles.file = ''; handles.path = '';
set([handles.savemenu handles.saveasmenu handles.workspacemenu],...
'Enable','off');
set([handles.rchi2edit handles.dofedit handles.eta2edit],'String','');
[names,data] = fillTable('result',0,-Inf,Inf,true);
handles.resulttable.getModel().setDataVector(data,names);
clearplot(handles.residualplot)
axes(handles.residualplot)
set(handles.residualplot,'Box','on');
clearplot(handles.fitplot)
axes(handles.fitplot)
set(handles.residualplot,'Box','on');
% If there was an error
if ~handles.dataPlottable
loadbutton_Callback(handles.loadbutton,[],handles);
clearplot(handles.fitplot);
% Plot the data
else
plot(handles.xdata(:,1),handles.ydata(:,1),'.');
xlabel('x1');
ylabel('y');
end
guidata(handles.figure1,handles);
% --- Checks existence of a variable in the workspace ---------------
% also checks if variable is numeric
function bool = existsInBase(name)
bool = evalin('base',['exist(''' name ''',''var'')']) &&...
evalin('base',['isnumeric(' name ')']);
% --- Clears the plot -----------------------------------------------
function clearplot(handle)
axes(handle);
cla
xlabel('');
ylabel('');
warning off
legend({});
warning on
% --- Conditions data ----------------------------------------------
function handlesOut = conditionData(handles)
entries = [handles.xdatamenu handles.dxdatamenu...
handles.ydatamenu handles.dydatamenu];
handles.dataPlottable = 1;
handles.dataFittable = 1;
handles.errorsGood = 1;
% Get the chosen variable names
menuValues = get(entries,'Value');
menuNames = get(entries,'String');
for i=1:length(menuNames)
names{i} = menuNames{i}{menuValues{i}};
if menuValues{i}>1
existing(i) = existsInBase(names{i});
if existing(i)
values{i} = forceColumn(evalin('base',names{i}));
sizes{i} = size(values{i});
else
values{i} = [];
sizes{i} = [0 0];
end
else
existing(i) = 0;
values{i} = [];
sizes{i} = [0 0];
end
end
% If y exists, rotate x and dx if necessary
if existing(3) && existing(1)
six = sizes{1}; siy = sizes{3};
if six(1)~=siy(1) && six(2)==siy(1)
values{1} = values{1}';
sizes{1} = size(values{1});
end
if existing(2)
sidx = sizes{2};
if sidx(2)~=siy(1) && sidx(2)==siy(1)
values{2} = values{2}';
sizes{2} = size(values{2});
end
end
elseif existing(3)
siy = sizes{3};
values{1} = (1:siy(1))';
elseif existing(1)
six = sizes{1};
values{3} = (1:six(1))';
else
handles.dataPlottable = 0;
handles.dataFittable = 0;
end
% Check error vectors
if ~all(existing)
handles.errorsGood = 0;
else
six = sizes{1}; sidx = sizes{2}; siy = sizes{3}; sidy = sizes{4};
if sidx~=six
if circshift(sidx,[0 1])==six
values{2}=values{2}';
else
handles.errorsGood = 0;
end
end
if sidy~=siy
if circshift(sidy,[0 1])==siy
values{4}=values{4}';
else
handles.errorsGood = 0;
end
end
end
%Assign data
handles.xdata = values{1}; handles.dxdata = values{2};
handles.ydata = values{3}; handles.dydata = values{4};
handles = setFitEnable(handles);
handlesOut = handles;
% ---- Forces vector to be a column vector -------------------
function vector = forceColumn(vectorIn)
vector = vectorIn;
si = size(vectorIn);
if si(1)==1
vector = vector';
end