@comment(Hey, EMACS, this is -*- SCRIBE -*- input)
@device(dover)
@make(6001)

@modify(excounter, numbered [Exercise @1], referenced [@1])

@PageHeading(left "6.001 -- Fall Semester 1985",
             center "@Value(Page)",
             right "Problem set 5")

@begin(center)
MASSACHUSETTS INSTITUTE OF TECHNOLOGY
Department of Electrical Engineering and Computer Science
6.001 Structure and Interpretation of Computer Programs
Fall Semester, 1985

Problem Set 5
@end(center)
@blankspace(0.25 in)

@begin(flushleft)
Issued: Tuesday, Oct. 8

Due (note the switch in due dates for morning and afternoon
recitations): 
@begin(itemize)
on Wednesday, Oct. 23
for recitations meeting at 12:00, 1:00, and 2:00

on Friday, Oct. 25
for recitations meeting at 9:00, 10:00, and 11:00
@end(itemize)

Reading assignment:
@begin(itemize)
finish chapter 2, section 3.1

programs (attached): @a[ps5-res.scm], @a[ps5-imp.scm]
@end(itemize)
@end(flushleft)


@b[Note:] This is a 2-week problem set, partially because of the quiz
next week and partially because of the difficulty of the problem set
itself.  We strongly suggest that you do not attempt to do the entire
problem set all at once.  It would be a good idea to plan for two
laboratory sessions: one for part 1 and one for parts 2 and 3.


@b[Quiz announcement:] Quiz 1 is on Thursday, October 17.  The quiz
will be held in Walker Memorial Gymnasium (50-340) from 5-7PM or
7-9PM.  You may take the quiz during either one of these two periods,
but students taking the quiz during the first period will not be
allowed to leave the room until the end of the period.  The quiz is
closed book, but you may bring with you one 8-1/2 by 11 inch sheet of
paper with notes that you have prepared in advance.  The quiz will
cover material from the beginning of the semester through problem set
4 and section 2.2 of the textbook.

@section(Series-Parallel Resistive Networks)

Louis Reasoner, appalled by his tuition bill, is trying to put his
computer background to good use by implementing an on-line electrical
circuit analyzer that he will rent out to 6.002 students.  Louis's
partner in this scheme is his roommate, Lem E. Tweakit, a 6-1 major
with a flair for hardware.  Lem suggests that they begin with a
program that computes resistances of simple ``linear resistive
networks.''@foot{In this problem set, we use the words ``network'' and
``circuit'' interchangeably.  In the context of Louis's program, there
is no distinction.} These are constructed from primitive elements
called @a[resistors].  A resistor is characterized by a number @a[R]
called the @a[resistance] of the resistor.  It can be depicted as
follows:
@begin(smallfigurebody)

                          o---/\/\/\/\----o
                                  R
@end(smallfigurebody)
For computational purposes, it is also convenient to consider the
@a[conductance] of a resistor, which is defined to be the reciprocal of
the resistance. (Conductance is traditionally denoted by the letter
@a[G].)
@begin(smallfigurebody)

                          o---/\/\/\/\----o
                                G = 1/R
@end(smallfigurebody)

A resistor is the simplest example of a kind of element called a
@i[two-terminal network], i.e., a network that has exactly two terminals to
which other objects can be connected:
@begin(smallfigurebody)
                               --------
                               |      |
                          o----|  N   |----o
                               |      |
                               --------

@end(smallfigurebody)

Networks can be combined by attaching their terminals together.  A
network has a resistance (and conductance) that is determined by the
resistances of its parts and the ways in which they are
interconnected.  Louis's and Lem's initial system will provide two
basic methods of combination for constructing two-terminal networks
from simpler two-terminal networks.  The first method is called @a[series]
combination:
@begin(smallfigurebody)
                       --------       --------
                       |      |       |      |
               o-------|  A   |-------|  B   |--------o
                       |      |       |      |
                       --------       --------

                             R = R  +  R
                                  A     B
@end(smallfigurebody)
The combined resistance of two networks connected in series is the sum
of the resistances.

The second method is @a[parallel] combination: 
@begin(smallfigurebody)
                    --------
                    |      |
               -----|  A   |-----                   
               |    |      |    |                   
               |    --------    | 
        o------|                |-----o         G = G  +  G
               |                |                    A     B
               |    --------    |                    
               |    |      |    |                     
               -----|  B   |-----     
                    |      |         
                    --------              
@end(smallfigurebody)                                            
The combined conductance of two networks joined in parallel is the sum
of the conductances of the pieces.

After a few hours of work, Louis and Lem have quite an
elegant program that handles such series and parallel
combinations.@foot{Networks that can be constructed as series and
parallel combinations of primitive two-terminal elements are called
@i[series-parallel networks].} The program is provided in the first
appendix to this handout, labeled @a[ps5-res.scm].

You should read through this code now to get some idea of how it
works.  There are constructors @a[make-resistor], @a[make-series], and
@a[make-parallel], and an operation called @a[resistance] that
computes the resistance of a network by finding the resistances of the
parts of a network and combining them appropriately.

Louis and Lem test their program by computing the resistance of the
following network:
@begin(smallfigurebody)

               R=10         R=30
    o---------\/\/\/-------\/\/\/--------o
         |                           |
         |           R=20            |
         |    -----\/\/\/-----       |
         -----|              |--------
              -----\/\/\/-----
                     R=40
@end(smallfigurebody)
They type:
@begin(programexample)

(define r1 (make-resistor 10))
(define r2 (make-resistor 30))
(define r3 (make-resistor 20))
(define r4 (make-resistor 40))

(define N
  (make-parallel (make-series r1 r2)
                 (make-parallel r3 r4)))

-->(resistance N)
10.000
@end(programexample)
Indeed, the answer is correct (as Lem verifies).

@begin(exercise)
Consider the data structure representing the network @a[N].
@begin(alphaenumerate)
Draw the box-and-pointer structure that represents the object @a[N].

How many times was @a[resistance] called in computing the resistance of
@a[N]?  What were the arguments to @a[resistance] for each call?  (To
specify the arguments, you can sketch the part of the network that
each argument represents, or you can give a description in English,
e.g., ``@a[R3] in parallel with @a[R4]''.)

@end(alphaenumerate)
@end(exercise)

@paragraph(L-extensions)

Given a 2-terminal network @a[B], we can form a new two-terminal
network by attaching an ``L-section,'' which is itself constructed
from two two-terminal networks @a[S] and @a[P] as follows:
@begin(smallfigurebody)
            -----
            |    |
    o-------| S  |-------------------
            |    |       |          |
            ------     ------     ------
                       |    |     |    |
                       | P  |     | B  |
                       |    |     |    |
                       ------     ------
                         |          |
    o--------------------------------
@end(smallfigurebody)
This operation is called ``L-extension'' of a ``base'' @a[B] by a ``series
part'' @a[S] and a ``parallel part'' @a[P].

@begin(exercise)
Write a procedure @a[L-extend] that takes three two-terminal networks --
@a[base], @a[series-part], and @a[parallel-part] -- and combines them
using @a[make-series] and @a[make-parallel] to produce the extended
network as shown above.
@end(exercise)

@begin(exercise)
Use your @a[L-extend] procedure to make a new network that extends the
network @a[N] from Exercise 1 (as the base) by a 10-ohm resistor (the
series part) and another 10-ohm resistor (the parallel part).

Verify that the resulting network has a resistance of 15 ohms.  Show
the expressions that you typed in order to generate the network and to
check its resistance.
@end(exercise)


By repeatedly L-extending a base by a given series and parallel
part, we obtain a circuit called a ``ladder.''  The following diagram
shows a 4-stage ladder.
@begin(smallfigurebody)

   o-----S---------S-------S-------S---------
              |        |       |       |    |
              P        P       P       P    B
              |        |       |       |    |
   o----------------------------------------- 

@end(smallfigurebody)

Louis decides that he can easily implement a ladder by making use of
his @a[L-extend] procedure, together with the @a[repeated] procedure
below:

@begin(programexample)
(define (repeated f n)
  (lambda (x)
    (if (= n 0)
        x
        ((repeated f (-1+ n)) (f x)))))
@end(programexample)

He defines the following procedure to construct a ladder with a
given number of stages:

@begin(programexample)
(define (ladder-extension stages base series-part parallel-part)
  ((repeated @i[<EXP-1>] stages) @i[<EXP-2>]))
@end(programexample)

@begin(exercise)
Complete the @a[ladder-extension] procedure.  What are the missing
expressions
@i[<EXP-1>] and @i[<EXP-2>]?
@end(exercise)

@begin(exercise)
A classical network-theory problem used to plague 6.002 students is to
compute the resistance of an infinitely long ladder of 1-ohm
resistors:  
@begin(smallfigurebody)
   o-----1-------- ...  ---1-------1---------
              |                |       |    |
              1    ...         1       1    1
              |                |       |    |
   o----------------------------------------- 
@end(smallfigurebody)
Use your @a[ladder-extension] procedure to generate longer and longer
ladders of this kind, and find their resistance.  (Show the
expressions you evaluated to generate the ladders.)  To what value
does the resistance appear to converge?  Where have you seen this
number before?
@end(exercise)

@begin(exercise)
Louis and Lem demonstrate their system to Ben Bitdiddle.  As an
example, they try to compute the resistance of a long ladder.  To
their surprise, they find that although the program gets correct
answers, it seems to run more slowly than before.

Taking a careful look at the code, they find that Louis has changed
the definition of @a[resistance-parallel] so that it now reads:
@begin(programexample)
(define (resistance-parallel ckt)
  (/ (* (resistance (left-branch ckt))
        (resistance (right-branch ckt)))
     (+ (resistance (left-branch ckt))
        (resistance (right-branch ckt)))))
@end(programexample)
Explain why this change makes the program run so slowly.  As an
example, consider a ladder in which each of the series, parallel, and
base pieces is a simple resistor.  If the ladder has @a[N] stages:
@begin(alphaenumerate)
How many resistors are contained in the ladder?

In computing the resistance of the ladder, how many times will the
procedure @a[resistance-resistor] be run if the system uses the
original version of the @a[resistance-parallel] procedure?

After Louis installs the new version of @a[resistance-parallel], how
many times will @a[resistance-resistor] be run in computing the resistance
of the @a[N]-stage ladder?  (You should be able to obtain an exact answer
in terms of @a[N].  However, partial credit will be given for good partial
answers.  Show your work.)

How has Louis's change affected the running time of the system?
(E.g., slowed it down by a constant factor?  slowed it down
quadratically? slowed it down exponentially?)
@end(alphaenumerate)
@end(exercise)


@section(General Series-Parallel Circuits)

Louis goes off to try to rent out the system in the 6.002 lab in
building 38.  His first potential customer is Anna Logg, a sophomore
who is busily doing her 6.002 problem set.  ``Foo,'' says Anna, ``this
is useless unless it can handle capacitors and inductors as well as
resistors.''

Louis goes back home and asks Lem for a short course on circuit
theory.  Lem says that indeed, interesting circuits have elements
called capacitors and inductors, and solving such circuits involves
(ugh!) differential equations, and Lem is too tired to
give a course in circuit theory (Louis has to take 6.002 next semester
anyway).  But luckily, Lem explains, it is unnecessary to understand
this in order to make a useful system.  In particular, explains Lem,
capacitors and inductors can be thought of as ``resistors'' whose
``resistance'' (actually called @i[impedance]) is a complex number that
varies with a parameter @a[s], called the ``complex frequency.''

Lem says that for a capacitor of capacitance @a[C] (measured in farads)
the ``resistance'' is 1/@a[sC].  For an inductor of inductance @a[L] (measured
in henrys) the ``resistance'' is @a[sL]:
@begin(smallfigurebody)

        C                             1/sC
      ||                              
 -----||-----  is viewed as  o---/\/\/\/\---o
      ||


          L                           sL

 ---      ---  is viewed as    o---/\/\/\/\---o
@end(smallfigurebody)

For example, a so-called ``parallel resonant circuit'' can be thought
of as a network of 3 ``resistors'' and analyzed in the same way as
before.  Only this time, the ``resistance'' of the network will be a
function of @a[s].
@begin(smallfigurebody)
-----------------                 -----------------
    |     |     |                     |     |     |
    \           |                     \     \     \
    /          ---                    /     /     /
    \R         ---   is viewed as     \R    \sL   \1/sC
    /           |C                    /     /     /
    |     |L    |                     |     |     |
-----------------                 -----------------
@end(smallfigurebody)

Louis meditates on this for a few days and turns for help to the great
wizard Alyssa P. Hacker.  Alyssa suggests a clever scheme that allows
Louis to handle these things without greatly changing his program.
The idea is that the ``resistance'' of a circuit (or a circuit
element), instead of being a simple number,
will now be a procedure that takes some @a[s] as argument.
For instance, the ``resistance'' of a
(real) resistor of declared resistance @a[R] is represented by a
procedure that computes a constant function whose value is @a[R], for
any @a[s].  For a capacitor of capacitance @a[C], the ``resistance''
is the procedure that takes @a[s] as argument and returns 1/@a[sC].

The data structure that Alyssa suggests for representing a primitive
electrical element is a list of three elements
@begin(example)
(<type> (<parameter name> <value>) <procedure>)
@end(example)
This includes a @a[type] as in Louis's original program, a
``documentation field'' that makes printouts of circuits easier to
interpret, and the actual ``resistance'' procedure of the element.

Thus, a (real) resistor is constructed by
@begin(programexample)

(define (make-resistor resistance)
  (attach-type 'resistor
               (list (list 'resistance resistance)
                     (lambda (s) resistance)))))
@end(programexample)
A capacitor is constructed by
@begin(programexample)
(define (make-capacitor capacitance)
  (attach-type 'resistor
               (list (list 'capacitance capacitance)
                     (lambda (s)
                       (/ 1 (* capacitance s))))))
@end(programexample)
(Note that, as far as the @a[type] is concerned, a capacitor is a type
of resistor, although we can tell the difference by looking at the
documentation field.)  Inductors are constructed similarly.

Then, for example, the ``resistance'' of a series combination should
be a procedure that, given an argument @a[s], computes the
resistances of the branches for that @a[s], and returns their sum:
@begin(programexample)

(define (resistance-series ckt)
  (lambda (s)
    (+ ((resistance (left-branch ckt)) s)
       ((resistance (right-branch ckt)) s))))
@end(programexample)

Actually the procedures given above are not quite correct, because in
order to use this idea to get information about circuits, one needs to
use inputs @a[s] that are complex numbers.  (Ask Lem if you want to
know why, or take 6.002.)

So Louis has to make a further modification: All arithmetic must be
changed so that it works with complex numbers.  He builds a
representation for complex numbers and complex number operations and
modifies the code accordingly.  The result of his labors (which works,
amazingly!) is in the file @a[ps5-imp.scm].  This is attached as the
second appendix to this problem set.  You should study the code and
use it to do the following exercises.

@begin(exercise)
Load Louis's new program into Scheme.@foot{A version of this file
should have been copied to your disk when you invoked the ``load
problem set'' command.  You can now load this into Scheme by
evaluating the Scheme expression @a[(load "ps5-imp")].  Or, if you
prefer, you can load @a[ps5-imp.scm] into an NMODE buffer and transfer
it to Scheme from there.} Build a parallel resonant
circuit (as shown above) using a 2-ohm resistor, a 1-farad capacitor,
and a 1-henry inductor.  Call the circuit @a[N2].  What is the impedance
(``resistance'') of @a[N2] at @a[s=2+0j]?, at @a[s=0+2j]?  (Note that
Louis's code contains a constructor that makes complex numbers of the
form @a[A+Bj], where @a[j] is the square root of -1.)  Show the
expressions you evaluated in order to generate the circuit and to find
the impedance.
@end(exercise)

Lem has suggested that it is very useful to look at a plot of the
magnitude of the impedance of a circuit as a function of imaginary
values of the complex frequency @a[s] (i.e., values of @a[s] for which
the real part is 0).  Such a plot is called a @i[frequency response]
plot of the circuit.

We have supplied a program that plots functions on the Chipmunk
screen.  To get an idea of how the program works, you should load the
file @a[ps5-graph.scm] and type @a[(line-plot square -2 2 .1)].  This
plots values of @a[square] in the interval from -2 to 2 with an
increment of 0.1.  Notice that @a[line-plot] prints the @a[x] and
@a[y] bounds of the plot, in the format @a[(xmin xmax ymin ymax)].
(The file @a[ps5-graph.scm] will be transferred to
your local disk as part of the code for this problem set.  You need
not read or understand the code in the file in order to use
@a[line-plot].)

@begin(exercise)
Make a plot of the frequency response of the circuit @a[N2] from
@a[s=0+.001j] to @a[s=0+2j], stepping @a[s] by @a[.05j].  (Note that
the @a[resistance-2] file contains a @a[magnitude] procedure for computing
magnitudes of complex numbers.)  You should get a curve like the one
shown below.  The sharp peak is characteristic of a ``resonant
circuit,'' and the value of @a[s] where the peak occurs is called the
@i[resonant frequency].  Where is the resonant frequency for the
circuit @a[N2]?  What expressions(s) did you type to Scheme to
generate the plot?
@end(exercise)

@blankspace(4.5 inches)

@begin(exercise)
Use Louis's program to determine how the frequency response changes
if you change the resistance of the resistor from 2 to 10 ohms.
Sketch the new frequency-response curve, indicating how it differs
from the original curve.
Does the resonant frequency change?  Does the height of the peak
change?
@end(exercise)

@section(Generating Circuits)

Louis's program is so successful that it becomes standardly used by
almost all students taking 6.002.  Eventually, the Course 6 faculty
realizes that students are doing circuit-analysis homework simply by
using the program, resulting in the shocking state of affairs that
most students are able to spend less than 30 hours per week doing
6.002 problem sets!  At an emergency meeting of the Course 6
Undergraduate Educational Policy Committee, Prof. Gould suggests that
the 6.002 staff can increase the difficulty of the problem sets by
asking students to @i[design] circuits rather than simply to analyze
them: Students will be given a list of parts (resistors, capacitors,
and inductors) and asked to use these to build a circuit that has some
desired behavior, such as resonance in a given frequency range.

When Louis hears of this plan, he decides to augment his program so
that it can do this kind of problem as well.  His idea is to write a
program that will randomly generate series-parallel circuits using the
specified parts, then analyze the circuits to find ones with the desired
behavior.  (Each of the specified parts will be used exactly once in
each circuit.)

We can generate a random circuit as follows: Suppose that @a[S] is the
set of parts we are supposed to use.  We split @a[S] (randomly) into
two subsets @a[A] and @a[B].  Then, recursively, we generate a random
circuit @a[CA] using the parts in @a[A] and a random circuit @a[CB]
using the parts in @a[B].  Finally, we return either the series
combination of @a[CA] and @a[CB] or the parallel combination of @a[CA]
and @a[CB] (each with probability 1/2).

@begin(exercise)
Write a procedure @a[random-circuit] that uses this strategy to
generate random series-parallel circuit.  @a[Random-circuit] should
take as argument a set of parts, represented as a list.  You should
use a separate procedure called @a[split-list], which takes as
argument a list @a[S] and returns a pair of lists @a[A] and @a[B],
such that each item in @a[S] appears either in @a[A] or in @a[B] (with
equal probability).  @a[Random-circuit] is then a recursive procedure
that uses @a[split-list].  (Think about how to stop the recursion:
What do you do if one of the lists @a[A] or @a[B] is empty, or has
only one element?)  Turn in a listing of your procedure(s).
@end(exercise)

@begin(exercise)
Use your program to generate five different circuits whose parts are a
1-farad capacitor, a 2-farad capacitor, a 1-henry inductor, a 1-ohm
resistor, and a 2-ohm resistor.  Turn in the (pretty-printed) results.
@end(exercise)

@begin(exercise)
Using the plotter, determine which (if any) of the circuits you made has a
resonant frequency between @a[s=0] and @a[s=0+2j].  (Resonant behavior
is characterized by a sharp peak or a sharp dip at one or more
frequencies.)
@end(exercise)

@b[Program design:] Do either one of the next two exercises.

@begin(exercise)
Describe carefully how you would write a program that automatically
tests circuits for resonant behavior.  You needn't actually implement
the program, but if you do, and have a lot of time, search for
series-parallel circuits with four components to find those with
resonant behavior.  Take the four components to be a 10-ohm resistor,
a 2-farad capacitor, a 1-farad capacitor, and a 2-henry inductor.
Search the region from s=0 to s=0+2j, as before.
@end(exercise)

@begin(exercise)
One problem with the random method of generating circuits is that the
same circuit might be generated many times, and some circuits might
not be tried at all.  How would you write a program to generate
@i[all] the circuits that can be constructed from a given list of
parts (with each part used exactly once)?  Notice that there are
really two problems here: one is to make sure that each circuit is
generated, and the other is to eliminate duplicates.  Eliminating
duplicates can be tricky: for instance, given parts @a[p1], @a[p2],
and @a[p3], the combination
@begin(programexample)
(make-series p1 (make-series p2 p3))
@end(programexample)
represents the same circuit as
@begin(programexample)
(make-series p2 (make-series p1 p3))
@end(programexample)
You needn't write any code for this exercise, but be sure to give a
good description of how you would go about solving the problem.

Warning/Hint: There is a clever way to eliminate the duplicates, but a
straightforward approach can become extremely complicated.  Don't be
afraid to turn in a partial solution if you don't discover a complete
method.  Also, if you actually implement a program and want to test
it, you should know that there are 8 distinct circuits that can be
constructed with 3 parts, 52 with 4 four parts, and 472 with 5 parts.
@end(exercise)

@newpage()
@begin(programexample)
@include(ps5-res.SCM)
@end(programexample)
@newpage()
@begin(programexample)
@include(ps5-imp.SCM)
@end(programexample)
