Showing posts with label MATLAB. Show all posts
Showing posts with label MATLAB. Show all posts

Tuesday, August 23, 2011

The maximum number of matrix dimensions in MATLAB

[ Background: I was asked what the maximum number of matrix dimensions was in MATLAB today. I responded as follows. ]

You are only limited by the amount of memory available and the maximum number of ELEMENTS (as opposed to dimensions) in a matrix. The actual number of dimensions is just a detail about how the memory is indexed. You can reshape any existing matrix to any number of dimensions (I'll give details below). You can only create a new matrix if it abides by the memory and element limits that vary by computer.

To find out the maximum number of elements for a matrix on your computer, use the MATLAB command "computer" (do "help computer" for details). For example:
[~,maxsize,~]=computer
tells me that I can have 2.8147e+14 elements in matrices on my computer. So I better be sure that:
(number of rows)
   × (number of columns)
   × (number of cubes)
   × (number of 4-th dimensional thinggies)
   × (...)
is less than that number.

To find out about memory limits on your system see, the command "memory" ("help memory" or "doc memory"). Unfortunately, memory may not be available on your system. Alternatively, you can see:

http://www.mathworks.com/support/tech-notes/1100/1110.html

for information about memory limits in MATLAB. For information about the maximum number of elements (and the command "computer" that I discussed above), see (UPDATE: MATLAB has moved this page, and this link doesn't land in the right spot anymore):

http://www.mathworks.com/support/tech-notes/1200/1207.html#15

Regarding dimensions, you can use the command "reshape" to re-index any existing matrix. For example, if I start with the column vector:
A=ones(100,1)
I can turn it into a row vector:
newA = reshape(A, 1, 100)
or a matrix of any number of dimensions so long as the number of elements is still 100.
newA = reshape( A, 2, 2, 25 )
newA = reshape( A, 1, 1, 1, 1, 1, 1, 1, 1, 1, 100, 1 )
newA = reshape( A, 1, 1, 1, 2, 1, 50, 1, 1, 1, 1, 1, 1, 1, 1 )
% etc.
Now, I'm assuming you're using regular MATLAB matrices. Alternatively, you can use sparse matrices so long as you limit yourself to functions that work with sparse matrices:
help sparfun
A sparse matrix stores an index with every element. That lets it "skip over" the 0 elements of the matrix. Consequently, you can store VERY large matrices with an abstract number of elements far larger than anything you can work with in MATLAB... however, most of those abstract elements will be 0.

Thursday, July 07, 2011

Someone asked me about Hilbert transforming minimum-phase magnitude responses today...

Someone sent me this e-mail today:
Thank you for contributing to the Wikipedia article about minimum phase. I gather from the article that I should be able to use the Hilbert transform to compute a phase response from the amplitude response of a minimum phase system. Yet when I compute (in Matlab) the Hilbert transform of the log of the amplitude response of a Butterworth filter (sampled at uniform frequency intervals), the result is not real and does not resemble the phase response of a Butterworth at all. I expected that it would equal the phase response of a Butterworth since a Butterworth is minimum phase. What have I missed? Thank you.
So I responded in an e-mail, and I've pasted that e-mail here.
Assuming that you are using a high-order filter, are you unwrapping your phase? See the MATLAB function "unwrap" for details. Another easy fix is to ensure you're using the NATURAL log to extract the exponent of the magnitude as an exponential. In MATLAB, "log" is natural log and "log10" is common log.

If you still have the problem, make sure your filter is truly minimum
phase. In particular, the transfer function and its inverse must be
stable and CAUSAL. The causality condition is redundant so long as your
notion of stability includes poles induced from unmatched zeros. For
example, the discrete-time filter:
z + 0.5
is not causal and thus has a pole at infinity. So it does not meet the criteria for being minimum phase. On the other hand, the filter:
(z+0.5)/z
is minimum phase. So let's take its impulse response. In MATLAB, you could try:
h = impulse(tf( [1,0.5], [1,0], 0.1));
or...
z = tf('z');
h=impulse( (z+0.5)/z );
or just read it from the numerator and add as many zeros as you'd like...
h=[1,0.5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0];
Then use the FFT:
H=fft(h);
Then use the discrete-time Hilbert transform of the NATURAL log:
X=hilbert(log(abs(H)));
Then, to compare, use "plot":
plot( 1:length(h), -imag(X)*180/pi, 'o', ...
      1:length(h), angle(H)*180/pi, 'x' )
I think you'll find that each x is circled.

To summarize:
h=[1,0.5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0];
H=fft(h);
X=hilbert(log(abs(H)));
plot( 1:length(h), -imag(X)*180/pi, 'o', ...
      1:length(h), angle(H)*180/pi, 'x' )
Here's another interesting case that won't match as well because of the discrete-time approximation.
z = tf('z',1);
H = (z + 0.5)/(z);
[mag,phase,w]=bode(H);
mag=mag(:); phase=phase(:); w=w(:);
X=hilbert(log(mag));
plot(w/pi,-imag(X)*180/pi,w/pi,phase)
As you can see, these two match pretty well in the interior region. You can make some interesting observations about the edges where they don't match well.

Monday, March 07, 2011

MATLAB script to prove to you that the perfect squares are the switches that are toggled

Evidently, this problem exists in both "switch" and "locker" forms. Assume that there are 100 switches are initially off and numbered from 1 to 100.
  • First, you toggle multiples of 1 (i.e., every switch gets turned on),
  • and then you toggle multiples of 2 (i.e., every even-numbered switch gets turned off),
  • and then you toggle multiples of 3 (i.e., switches 3, 6, 9, 12, ... are toggled),
  • ...
  • and then you toggle multiples of 99 (i.e., switch 99 is toggled),
  • and then you toggle multiples of 100 (i.e., switch 100 is toggled).
After this process, which switches are "on"?

Examining the problem, you notice that switches are toggled the same number of times as they have unique factors. For example, switch 6 is toggled 4 times, which corresponds to its unique factors, 1, 2, 3, and 6. However, switch 4 is toggled only 3 times, which corresponds to its unique factors, 1, 2, and 4 (i.e., even though 4 = 2 × 2, the factor 2 is only represented in the list once because it is duplicated). Moreover, it is only the perfect squares (1, 4, 9, 16, 25, 36, 49, 64, 81, 100) that will have repeated factors (i.e., their whole square roots), and so it is only those switches that will be flipped and odd number of times. Furthermore, it is only those switches that will be "on" after this game.

To verify this to yourself, use a MATLAB script:
% All switches initially off
switches = zeros(1,100);

% Toggle multiples of the current switch, including the current switch
for i = 1:100
% These are the switches to toggle
ivec = i:i:100;

% xor'ing them with 1's toggles them
switches(ivec) = bitxor(switches(ivec), 1);
end

% Show the indexes of the switches that are "on" after this process
% (expect perfect squares: 1 4 9 16 25 36 49 64 81 100)
find( switches )
Running the script results in:
ans =

1     4     9    16    25    36    49    64    81   100
which is what we expected.

Of course, you could also keep track of how many times you made a flip. XOR can help with this too.
% All switches initially off
switches = zeros(1,100);
new_switches = zeros(1,100);
num_flips = zeros(1,100);

% Toggle multiples of the current switch, including the current switch
% (gratuitous use of bitxor as a demo)
for i = 1:100
% These are the switches to toggle
ivec = i:i:100;

% xor'ing them with 1's toggles them
new_switches(ivec) = bitxor(switches(ivec), 1);

% count the flips
% ( num_flips(ivec) = num_flips(ivec)+1  works too)
num_flips = num_flips + bitxor( new_switches, switches );

% commit new_switches to switches to maintain invariant
% (can omit new_switches by not using second bitxor above)
switches = new_switches;
end

% Show the indexes of the switches that are "on" after this process
% (expect perfect squares: 1 4 9 16 25 36 49 64 81 100)
find( switches )
Then, from the console, we can issue commands like:
num_flips( find(switches) )     % shows number of flips for switches ending "on"
num_flips( find(switches==0) )  % shows number of flips for switches ending "off"
find( mod(num_flips,2)==1 )     % verifies odd-count flips have perfect-square indexes
So that's fun.

Tuesday, January 04, 2011

Drawing a circle in MATLAB (curved rectangle instead of approximating with filled polygon)

Today, someone I know googled for some help drawing a filled circle in MATLAB and asked me what I thought of the code that was found. The code found, which seems to be the conventional way to draw circles in MATLAB according to Internet posters, generates a polygonal approximation of a circle and then fills it. It goes something like this (where you can use linspace(0,2*pi,N+1) and daspect([1,1,1]) (or axis square) instead of two of the commands used here):
xc = 0;
yc = 0;
r = 2;
N = 256;
theta = (0:N)*2*pi/N;
x = r*cos(theta) + xc;
y = r*sin(theta) + yc;
fill(x, y, 'k');
axis equal;
where (xc,yc) is the coordinate of the center of the circle, r is the radius of the circle, and (N-1) is the number of sides of the polygon that is inscribed inside the desired circle (i.e., the polygon's corners intersect the circle). The circle is filled with the color black, which corresponds to the 'k' parameter of the fill command. To quickly get rid of the circle without clearing the figure, change the fill command so its figure handle gets assigned to a variable (e.g., h = fill(x,y,'k');) and you can delete that figure handle later (e.g., delete(h);). I notice that some people precede the fill command with a plot(x,y); command, but that is not necessary; the fill command will construct the closed finite polygon and put a (black) border around it by default, and so there is no need to draw the border ahead of time with plot.

[ Of course, there are methods very similar to the above that use a rotating complex phasor (i.e., a complex exponential from r*exp(i*theta)) and then let plot generate the projected Cartesian plane coordinates from the complex plane. These methods are equivalent but depend on MATLAB to do the trigonometry behind the scenes, which may or may not be faster... ]

What surprises me is that there is a perfectly nice built-in MATLAB command that will draw rectangles, rounded rectangles, circles, and ellipses very quickly essentially without having to approximate them with finite polygons. It is the rectangle command. Some of the people who posted code like the above example apparently know about the rectangle command (because they use it to verify that their circular approximation is sufficiently circular); however, I guess it's not ideal for them because it references the figure to a corner as opposed to its center. Nevertheless, a lot of people who might not be as picky may not know about the rectangle command, and so here's an example similar to the above.
w = 4;
xc = 0;
yc = 0;
rectangle('Curvature',[1 1], ...
          'Position',[xc-w/2 yc-w/2 w w], ...
          'FaceColor','k');
axis equal;
The 'Curvature' property tells MATLAB that the lines connecting the corners of the rectangle should have no straight portions. By decreasing each of the 1's to 0 in the curvature property, the circle looks more like a rounded rectangle with flat sides and rounded corners. The 'Position' property places the bottom-left corner of the rectangle in its first two elements and then sets the width and height of the "rectangle". So here, a circle centered at (xc, yc) is drawn with a radius of w/2. The circle is filled with 'FaceColor'. Again, axis equal; could be replaced with daspect([1,1,1]); or axis square;. Additionally, rectangle can be assigned to a variable (e.g., h = rectangle(...);) so that the rectangle can be easily removed later (e.g., delete(h);) without having to clear the figure. So this example draws a circle, but by playing with the rectangle parameters, it is easy to draw rectangles, rounded rectangles, and ellipses as well.

It is possible that the former method might be faster, but I can't imagine it would be much faster. The latter method seems more elegant and intuitive to me, and I have to imagine that's what the people at Mathworks would advise you to use if you called them up. Having said that, I have not called them up, and so you decide for yourself.

Wednesday, March 11, 2009

"Interference" seems like an appropriate name

I got a request from a marketer that I link to his product from this blog:From him:
Inference is a Microsoft Office add-in that has a point-and-click interface for adding MATLAB code, .m files, and structured data to Word and Excel documents. If you use Microsoft Office, Inference is an alternative to EX Builder and Notebook for generating reports, reproducible research, and Office applications. And regardless of whether you use Office, Inference has an integrated development environment (Inference Studio) with an intelligent editor that features breakpoints and edit-and-continue.
I have no idea why you would possibly need such a thing. I view software like this as predatory—it preys on people who think they're helpless. They end up overlooking existing easy solutions (like using the debugging features of MATLAB's own editor) and get distracted from finding truly good solutions.

Perhaps I'm being a little too harsh. Interference's own website gives a better description of its features:
Inference for MATLAB allows you to:
  • Execute MATLAB code directly inside of Microsoft Word to create formatted reports that contain explanatory text and graphical/code output.
  • Execute MATLAB code directly inside of Microsoft Excel to create dynamic spreadsheets that leverage existing Excel functionality.
  • Store all of your MATLAB code, data, and M-Files inside a single Microsoft Word and Excel document.
If you have use for such a thing... Give it a whirl, but don't come to me for help.

(by the way, my LaTeX build environment does the equivalent of "Interference for MATLAB" for LaTeX users. Among other things, if you \includegraphics{image_name} and the build environment finds an image_name.m file, it will automatically generate an image_name.eps (or image_name.pdf) whenever the MATLAB script gets updated)

Tuesday, August 19, 2008

Building MATLAB images from Makefiles

Recently I beefed up my LaTeX Makefiles to make image generation more automatic. These implicit rules have worked very well for me (note that the indentation is done with TABS — that's important).
%.pdf : %.eps
epstopdf $*.eps

%.eps : %.m
matlab -nosplash -nodesktop -r "$*;quit"

%.eps : %.png
convert -density $(RESOLUTION) $*.png $*.eps

%.eps : %.jpg
convert -density $(RESOLUTION) $*.jpg $*.eps

%.eps : %.dvi
dvips $* -D $(RESOLUTION) -E -o $*.eps

%.dvi : %.tex
latex -interaction=nonstopmode $*.tex
rm -f $*.log
rm -f $*.aux
That if, if I need a file called FILENAME.eps or FILENAME.pdf, the Makefile will look for FILENAME.m, FILENAME.png, FILENAME.jpg, or FILENAME.tex. Depending on which one it finds, it will run the appropriate command to generate the image I need. Note that I define RESOLUTION=1500 in the top of my Makefile.

I want to point out the MATLAB build line:
matlab -nosplash -nodesktop -r "$*;quit"
That handy line starts up as little of MATLAB as possible and runs a script that contains something like:
% First, generate a figure somehow

% Next, setup the figure's print proportions
set( gcf, 'PaperType', 'usletter', ...
'PaperOrientation', 'portrait', ...
'PaperPosition', [0.0 3.5 11 3.5] );

% Finally, save the figure as a (color) EPS
saveas( gcf, 'FILENAME.eps', 'epsc2' );
After the script finishes, MATLAB exits. It won't get started on any subsequent makes so long as the script file doesn't change or the image isn't deleted.

Getting back to the more general case, up high in my Makefile, I have something like
RESOLUTION=1500
BASETEXIMAGES=$(shell perl -ne \
'/\\includegraphics\s*(?:\[.*?\]|)\s*{\s*(.*?)\s*}/ \
&& do { print "$$1 " \
if (-e "$${1}.tex" || \
-e "$${1}.png" || \
-e "$${1}.jpg" || \
-e "$${1}.m"); };' *.tex)
TEXIMAGES=$(addsuffix .eps,$(BASETEXIMAGES))
and a little later I make $(TEXIMAGES) a dependency for my document (or something equivalent to that).

Those lines have saved me a lot of time, and I'm pretty happy about them.

Friday, June 27, 2008

Interpolating for Zero-Crossing Detection in MATLAB

Today someone who read my post on sinc interpolation in MATLAB e-mailed me to ask about how to interpolate between data points in MATLAB to do accurate zero-crossing detection. Here, I summarize my response.

Detecting zero crossings of (nearly) square waves that are sampled is difficult to do accurately because it pushes the limits of the "band-limited" signal approximation we use when sampling. That being said, if you'd like to use sinc interpolation, you should decide *HOW* close you need to be to the zero crossing. For example, your original signal might have 50 picoseconds between samples, and so without interpolation, you can detect zero crossings accurate within 50 picoseconds (ps). Maybe you need a closer result. How close do you need to be? 10ps? 5ps? 1ps? Less than that?

Figure out how close you need to be and then generate the appropriate upsampled time vector:
uptime = 0:step_size:final_time;
where step_size is your tolerance (e.g., 5 ps) and final_time is the maximum value of your sampled time vector. It is a good idea to engineer your step_size and final_time so that length(uptime)/length(old_time) is an INTEGER. It's also a good idea to make sure that all of your old_time points (except maybe your last point) exist in your uptime vector (i.e., your uptime vector just has time ADDED between old_time points).

Then you can use any interpolation mechanism you'd like. If you want to use the sinc_interp function that I wrote, do something like:
new_values = sinc_interp( old_values, old_time, uptime );
If you use sinc_interp, make sure you use the VECTORIZED version, which is MUCH faster. Actually, MATLAB provides a MUCH faster function that does NEARLY the exact same thing — the interpft command. In that case, you do
new_values = interpft( old_values, length(uptime) )
Otherwise, you might have luck using the resample command in MATLAB (it does NOT do pure sinc interpolation; it's more sophisticated, and it may not match at every data point):
new_values = resample( old_values, P, Q );
where P/Q is the INTEGER representing length(new_time)/length(old_time) (again, engineer your step_size to make this possible). ALTERNATIVELY, interp works OK too:
new_values = interp( old_values, P/Q );
Try all three and see which one works best for you.

Once you have this new interpolated vector, use some mechanism to detect zero crossings. Off the top of my head, here's the fastest way I can think of to do the zero-detection. Assume that we have
  • y (column vector of data -- e.g., new_values)
  • t (column vector of time -- e.g., uptime)
Also assume that t is already sorted. Then... (note that union sorts too)
i = union( find(y==0), find(conv(sign(y),[1 1]) == 0) );
should give you the MATLAB indexes where y crosses zero.

To test, try...
plot( t, y );
hold on;
stem( t(i), max(y)*ones(size(i)) );
hold off;
The result should be a plot of your data with spikes added at each zero crossing. That example also demonstrates how you recover the particular zero-crossing times with
t(i)
For example, when I apply this algorithm to a sin(t) waveform, t(i) gives me 0, pi, 2*pi, and 3*pi out, as expected.

Thursday, June 26, 2008

sinc interpolation in MATLAB

UPDATE: The examples given here are meant to give mathematical insight into how sinc interpolation works by using a finite-time APPROXIMATION. Sinc interpolation necessarily involves a circular convolution, which is not a finite computation in the time domain. If you actually need to do sinc interpolation, use the interpft function. It does an FFT, pads the FFT with zeros, and does an IFFT. Consequently, it is VERY FAST. Moreover, using an FFT (or DFT, in general) is the only way to use a finite computation to do a sinc interpolation (or circular convolution in general). Just make sure that the resampled time vector you use has a length that is an integer multiple of your original time vector. Also make sure that it lands on the same points as your original time vector (i.e., it should only add new points between old points).

I've also created a interpftw that does the same job as interpft but allows you to reconstruct samples from different aliasing windows. Whereas interpft pads the end of the FFT with zeros, interpftw pads both the beginning and the end. In other words, the former is a low-pass filter and the latter is a bandpass filter.
If you search Google for sinc interpolation in MATLAB, many pages will reference the sinc_interp example from John Loomis. Unfortunately, I've found few sites that recognize that the function is not meant to do general purpose sinc interpolation. That is, it makes a few assumptions about the sampling rates that may not be evident to the average user.

So, I'm giving some of my students this example:
% Ideally "resamples" x vector from s to u by sinc interpolation
function y = sinc_interp(x,s,u)
    % Interpolates x sampled sampled at "s" instants
    % Output y is sampled at "u" instants ("u" for "upsampled")


    % Find the sampling period of the undersampled signal
    T = s(2)-s(1);

    for i=1:length(u)
        y( i ) = sum( x .* sinc( (1/T)*(u(i) - s) ) );
    end

    % Make sure y is same shape as u (row->row, col->col)
    y = reshape(y, size(u));
end
Here's a vectorized (i.e., MUCH FASTER) version:
% Ideally "resamples" x vector from s to u by sinc interpolation
function y = sinc_interp(x,s,u)
    % Interpolates x sampled at "s" uniformly spaced instants
    % Output y is sampled at "u" uniformly spaced instants
    % ("s" for "sampled" and "u" for "upsampled")
    % (consequently, length(x)=length(s))


    % Find the period of the undersampled signal
    T = s(2)-s(1);

    % The entries of this matrix are each u-s permutation.
    % It will be used to generate the sinc transform that will
    % be convolved below with the input signal to do the
    % interpolation.
    %
    % (recall that u(:) will be a column vector regardless
    % of the row-ness of u. So u(:) is a row, and s(:) is a
    % column)

    sincM = repmat( u(:), 1, length(s) ) ...
           - repmat( s(:)', length(u), 1 );

    % * Sinc is the inverse Fourier transform of the boxcar in
    % the frequency domain that was used to filter out the
    % ambiguous copies of the signal generated from sampling.
    % * That sinc, which is now sampled at length(u) instants,
    % is convolved with the input signal becuse the boxcar was
    % multipled with its Fourier transform.
    % So this multiplication (which is a matrix transformation
    % of the input vector x) is an implementation of a
    % convolution.
    % (reshape is used to ensure y has same shape as upsampled u)

    y = reshape( sinc( sincM/T )*x(:) , size(u) );
end
My function sinc_interp resamples the data in the x vector. The original time vector is given by the s vector and the new time vector is given by the u vector.

Alternatively, you can try this more advanced one that lets you pick which aliasing window you want to use to reconstruct (i.e., it's an ideal bandpass filter rather than just a low-pass filter).
% Ideally "resamples" x vector from s to u by sinc interpolation
function y = sinc_interp(x,s,u,N)
    % Interpolates x sampled sampled at "s" instants
    % Output y is sampled at "u" instants ("u" for "upsampled")
    % Optionally, uses the Nth sampling window where N=0 is DC
    %     (so non-baseband signals have N = 1,2,3,...)


    if nargin < 4
        N = 0;
    end

    % Find the period of the undersampled signal
    T = s(2)-s(1);

    for i=1:length(u)
        y( i ) = ...
            sum( x .* ...
                ( (N+1)*sinc( ((N+1)/T)*(u(i) - s) ) - ...
                  N*sinc( (N/T)*(u(i) - s) ) ) );
    end
end
Here's a vectorized (i.e., MUCH FASTER) version:
% Ideally "resamples" x vector from s to u by sinc interpolation
function y = sinc_interp(x,s,u,N)
    % Interpolates x sampled sampled at "s" instants
    % Output y is sampled at "u" instants ("u" for "upsampled")
    % Optionally, uses the Nth sampling window where N=0 is DC
    % (so non-baseband signals have N = 1,2,3,...)


    if nargin < 4
        N = 0;
    end

    % Find the period of the undersampled signal
    T = s(2)-s(1);

    % When generating this matrix, remember that "s" and "u" are
    % passed as ROW vectors and "y" is expected to also be a ROW
    % vector. If everything were column vectors, we'd do.
    %
    % sincM = repmat( u, 1, length(s) ) - repmat( s', length(u), 1 );
    %
    % So that the matrix would be longer than it is wide.
    % Here, we generate the transpose of that matrix.

    sincM = repmat( u, length(s), 1 ) - repmat( s', 1, length(u) );

    % Equivalent to column vector math:
    % y = sinc( sincM'(N+1)/T )*x';

    y = x*( (N+1)*sinc( sincM*(N+1)/T ) - N*sinc( sincM*N/T ) );
end

Wednesday, July 18, 2007

MATLAB Quick Reference Cards (and more)

UPDATE: I list an AMSTeX reference card below. There is also an AMSLaTeX reference card available at refcards.com.

This is meant to be a follow-up to the "TeX Reference Card (and others)" post.

I found a list of a bunch more quick reference cards, which include applications/packages like MATLAB, MATLAB toolboxes, Perl, MFC, MySQL, Linux, UNIX, Vi, Vim, Windows, AMSTeX, TeX, and a bunch more...

However, I was just looking for MATLAB quick reference cards. So, here are these (some of which did not come from the above site):
So, that's nice. I recommend one of the bottom two [i.e., 1, 2].

Tuesday, July 17, 2007

Using Skim with MATLAB

I found a hint for using TeXShop as a MATLAB previewer on OS X, primarily because plots look better in PDF and TeXShop auto-updates files from disk. This was a tip from back in 2005. Since then, MATLAB's OS X plotting has gotten a lot better. Additionally, Skim has been invented.

Because of the advances in MATLAB, this may not be a useful hint anymore, but give this a try sometime:
figure(1);
set(gcf, 'Visible', 'off');
plot(x, y);
print(gcf, '-dpdf', 'figure1.pdf');
system('open -a Skim figure1.pdf');
Now, I believe there's a way to do this without having to plot first. That is, I think there's a way to plot to PDF without first generating that figure.

What's cool about this is that any future updates of the PDF will cause Skim to update automatically. Therefore, Skim becomes your plot viewer. That might be nice, right?

Tuesday, June 26, 2007

Bounding Boxes and EPS to PDF Conversion (in LaTeX)

Another development: More bounding box related issues are discussed in this CTT thread. It turns out that dvips -E basically guesses RANDOMLY at what the bounding box should be, and so its answers can be inconsistent. GhostScript (gs) has a bbox driver that circumscribes your EPS with a rectangle and uses the rectangular dimensions as the bounding box. The epstool command can use this GhostScript calculation to update your EPS. So you can imagine doing things like...
latex file.tex
dvips -E file.tex -o tmp.eps
epstool --bbox --copy --ouput file.eps tmp.eps
epstopdf file.eps
The epspdf script has similar functionality (when you are converting from EPS to PDF) and will be included in TeXLive 2008.

Related post: LaTeX generated figures: Using preview instead of pst-eps

Follow-up: See another interesting option (pst-pdf) in this follow-up.

Update: Another interesting option is purifyeps, which requires pstoedit and Perl. See below.

None of the following is too special. This is all well-known stuff. However, it's not the easiest to find with a Google search, so I'm going to post it here.

Pretty often, I have to generate an EPS file with MATLAB. That EPS figure will go into a LaTeX document. To generate a PDF document from my LaTeX source, I will probably use PDFLaTeX/PDFTeX. However, that means I need to convert that MATLAB EPS figure to PDF. Most EPS-to-PDF distillers I use will mess up the bounding box information and the result of the conversion will be a FULL PAGE PDF rather than the nice tiny EPS figure.

Keep this scenario in mind and consider these notes:
(*) ps2pdf command: To convert EPS to PDF and maintain the proper bounding box, try including the "EPSCrop" GhostScript (GS) option:
ps2pdf -dEPSCrop blah.eps blah.pdf
Without the -dEPSCrop option, I get the full-page PDF from a MATLAB EPS. However, with the -dEPSCrop option, things work fine.

(*) epstopdf command: ALTERNATIVELY, try this line instead:
epstopdf blah.eps
This works for me. If it doesn't, try this (on Windows with MiKTeX 2.6):
epstopdf --gsopt=-dEPSCrop blah.eps
That also works for me.

(*) epstopdf LaTeX package: You may be interested in the epstopdf package which comes with the oberdiek bundle. It should be included in your LaTeX distribution. If not, install the oberdiek bundle. You can find information (and download) about these here:
From that last link, you'll find this epstopdf usage (this will work for the graphics package as well):
\usepackage[pdftex]{graphicx}
\usepackage{epstopdf}
Then you can include graphics two different ways:
% Way 1: Includes blah.eps. Will ALWAYS generate 
% blah.pdf regardless of whether it already exists.
\includegraphics{blah.eps}

% Way 2: Includes blah.eps. If blah.pdf DOES NOT EXIST,
% it will automatically be generated.
\includegraphics{blah}
There are configuration options too. Consider the following:
% The default eps to pdf rule
\DeclareGraphicsRule{.eps}{pdf}{.pdf}{`epstopdf #1}

% Alternative eps to pdf rule
\DeclareGraphicsRule{.eps}{pdf}{.pdf}
{`ps2pdf -dEPSCrop #1}

% A rule for converting gif to png using ImageMagick
% NOTE: The placement of the % signs IS important
\DeclareGraphicsRule{.gif}{png}{.png}{%
`convert #1 `basename #1 .gif`.png%
}

% The same gif-to-png rule for Windows
% (i.e., without basename support)
\makeatletter
\DeclareGraphicsRule{.gif}{png}{.png}{%
`convert #1 \noexpand\Gin@base.png%
}
\makeatother
FINALLY, if you want to add .gif to the list of extensions that the package graphicx (or graphics package) searches if the file extension is not given in \includegraphics, you can either use the command \GraphicsExtensions OR doing something like:
\makeatletter
\g@addto@macro\Gin@extensions{,.gif}
\makeatother
Leaving the file extension off of the \includegraphics macro makes a lot of sense; however, remember that epstopdf will only be run the first time latex or pdflatex gets run. If you want it to convert all of your graphics every run, be sure to leave the extensions on.

(*) purifyeps command: There is also purifyeps, which requires pstoedit and Perl. Taken from purifyeps's CTAN page:
While pdfLaTeX has a number of nice features, its primary shortcoming relative to standard LaTeX+dvips is that it is unable to read ordinary Encapsulated PostScript (EPS) files, the most common graphics format in the LaTeX world. purifyeps converts EPS files into a "purified" form that can be read by *both* LaTeX+dvips and pdfLaTeX. The trick is that the standard LaTeX2e graphics packages can parse MetaPost-produced EPS directly. Hence, purifyeps need only convert an arbitrary EPS file into the same stylized format that MetaPost outputs.
I haven't actually played with this at all. I recommend reading purifyeps.pdf for more information about why you want a "purified" EPS rather than some other format. I assume that the bounding box problem shouldn't be an issue here, but I have no idea.

(*) pstoedit command:
MPS files can be used DIRECTLY by BOTH latex and pdflatex (pdflatex does MPS-to-PDF conversion on-the-fly). You can easily convert EPS files to MPS files yourself as long as you have psttoedit (download and install it from the pstoedit page). Take a look at section 5.4 (MetaPost) of epslatex.pdf for information on that. From the instructions there:
pstoedit -f mpost graphic.eps graphic.mp
mpost graphic.mp
rename graphic.1 graphic.mps
That is, run psttoedit to convert to MP and then use mpost to create the MPS from the MP. Simple, huh? Now, does it fix the bounding box problem? As with the last bullet, I have no idea. Maybe someday I'll try this.

Hopefully some of that will be useful to someone; it will at least be a good reference for me. :)