[system] / trunk / pg / macros / parserFormulaUpToConstant.pl Repository:
ViewVC logotype

Annotation of /trunk/pg/macros/parserFormulaUpToConstant.pl

Parent Directory Parent Directory | Revision Log Revision Log


Revision 5440 - (view) (download) (as text)

1 : dpvc 5393 loadMacros("MathObjects.pl");
2 :    
3 :     sub _parserFormulaUpToConstant_init {FormulaUpToConstant::Init()}
4 :    
5 :     =head1 FormulaUpToConstant();
6 :    
7 :     ######################################################################
8 :     #
9 :     # This file implements the FormulaUpToConstant object, which is
10 :     # a formula that is only unique up to a constant (i.e., this is
11 :     # an anti-derivative). Students must include the "+C" as part of
12 :     # their answers, but they can use any (single-letter) constant that
13 :     # they want, and it doesn't have to be the one the professor used.
14 :     #
15 :     # To use FormulaWithConstat objects, load this macro file at the
16 :     # top of your problem:
17 :     #
18 : dpvc 5395 # loadMacros("parserFormulaUpToConstant.pl");
19 : dpvc 5393 #
20 :     # then create a formula with constant as follows:
21 :     #
22 :     # $f = FormulaUpToConstant("sin(x)+C");
23 :     #
24 :     # Note that the C should NOT already be a variable in the Context;
25 :     # the FormulaUpToConstant object will handle adding it in for
26 :     # you. If you don't include a constant in your formula (i.e., if
27 :     # all the variables that you used are already in your Context,
28 :     # then the FormulaUpToConstant object will add "+C" for you.
29 :     #
30 :     # The FormulaUpToConstant should work like any normal Formula,
31 :     # and in particular, you use $f->cmp to get its answer checker.
32 :     #
33 :     # ANS($f->cmp);
34 :     #
35 :     # Note that the FormulaUpToConstant object creates its only private
36 :     # copy of the current Context (so that it can add variables without
37 :     # affecting the rest of the problem). You should not notice this
38 :     # in general, but if you need to access that context, use $f->{context}.
39 :     # E.g.
40 :     #
41 :     # Context($f->{context});
42 :     #
43 :     # would make the current context the one being used by the
44 :     # FormulaUpToConstant, while
45 :     #
46 :     # $f->{context}->variables->names
47 :     #
48 :     # would return a list of the variables in the private context.
49 :     #
50 :     # To get the name of the constant in use in the formula,
51 :     # use
52 :     #
53 :     # $f->constant.
54 :     #
55 :     # If you combine a FormulaUpToConstant with other formulas,
56 :     # the result will be a new FormulaUpToConstant object, with
57 :     # a new Context, and potentially a new + C added to it. This
58 :     # is likely not what you want. Instead, you should convert
59 :     # back to a Formula first, then combine with other objects,
60 :     # then convert back to a FormulaUpToConstant, if necessary.
61 :     # To do this, use the removeConstant() method:
62 :     #
63 :     # $f = FormulaUpToConstant("sin(x)+C");
64 :     # $g = Formula("cos(x)");
65 :     # $h = $f->removeConstant + $g; # $h will be "sin(x)+cos(x)"
66 :     # $h = FormulaUpToConstant($h); # $h will be "sin(x)+cos(x)+C"
67 :     #
68 : dpvc 5394 # The answer evaluator by default will give "helpful" messages
69 :     # to the student when the "+ C" is left out. You can turn off
70 :     # these messages using the showHints option to the cmp() method:
71 :     #
72 :     # ANS($f->cmp(showHints => 0));
73 :     #
74 : dpvc 5440 # One of the hints is about whether the student's answer is linear
75 :     # in the arbitrary constant. This test requires differentiating
76 :     # the student answer. Since there are times when that could be
77 :     # problematic, you can disable that test via the showLinearityHints
78 :     # flag. (Note: setting showHints to 0 also disables these hints.)
79 :     #
80 :     # ANS($f->cmp(showLinearityHints => 0));
81 :     #
82 : dpvc 5393 ######################################################################
83 :    
84 :     =cut
85 :    
86 :     package FormulaUpToConstant;
87 :     @ISA = ('Value::Formula');
88 :    
89 :     sub Init {
90 :     main::PG_restricted_eval('sub FormulaUpToConstant {FormulaUpToConstant->new(@_)}');
91 :     }
92 :    
93 :     #
94 :     # Create an instance of a FormulaUpToConstant. If no constant
95 :     # is supplied, we add C ourselves.
96 :     #
97 :     sub new {
98 :     my $self = shift; my $class = ref($self) || $self;
99 :     #
100 :     # Copy the context (so we can modify it) and
101 :     # replace the usual Variable object with our own.
102 :     #
103 :     my $context = (Value::isContext($_[0]) ? shift : $self->context)->copy;
104 :     $context->{parser}{Variable} = 'FormulaUpToConstant::Variable';
105 :     #
106 :     # Create a formula from the user's input.
107 :     #
108 :     my $f = main::Formula($context,@_);
109 :     #
110 :     # If it doesn't have a constant already, add one.
111 :     # (should check that C isn't already in use, and look
112 :     # up the first free name, but we'll cross our fingers
113 :     # for now. Could look through the defined variables
114 :     # to see if there is already an arbitraryConstant
115 :     # and use that.)
116 :     #
117 :     unless ($f->{constant}) {$f = $f + "C", $f->{constant} = "C"}
118 :     #
119 :     # Check that the formula is linear in C.
120 :     #
121 :     my $n = $f->D($f->{constant});
122 :     Value->Error("Your formula isn't linear in the arbitrary constant '%s'",$f->{constant})
123 :     unless $n->isConstant;
124 :     #
125 :     # Make a version with an adaptive parameter for use in the
126 :     # comparison later on. We could like n0*C, but already have $n
127 :     # copies of C, so remove them. That way, n0 will be 0 when there
128 :     # are no C's in the student answer during the adaptive comparison.
129 :     # (Again, should really check that n0 is not in use already)
130 :     #
131 :     my $n0 = $context->variables->get("n0");
132 :     $context->variables->add(n0=>'Parameter') unless $n0 and $n0->{parameter};
133 :     $f->{adapt} = $f + "(n0-$n)$f->{constant}";
134 :     return bless $f, $class;
135 :     }
136 :    
137 :     ##################################################
138 :     #
139 :     # Remember that compare implements the overloaded perl <=> operator,
140 :     # and $a <=> $b is -1 when $a < $b, 0 when $a == $b and 1 when $a > $b.
141 :     # In our case, we only care about equality, so we will return 0 when
142 :     # equal and other numbers to indicate the reason they are not equal
143 :     # (this can be used by the answer checker to print helpful messages)
144 :     #
145 :     sub compare {
146 :     my ($l,$r) = @_; my $self = $l; my $context = $self->context;
147 :     $r = Value::makeValue($r,context=>$context);
148 :     #
149 :     # Not equal if the student value is constant or has no + C
150 :     #
151 :     return 2 if !Value::isFormula($r);
152 :     return 3 if !defined($r->{constant});
153 :     #
154 :     # If constants aren't the same, substitute the professor's in the student answer.
155 :     #
156 :     $r = $r->substitute($r->{constant}=>$l->{constant}) unless $r->{constant} eq $l->{constant};
157 :     #
158 :     # Compare with adaptive parameters to see if $l + n0 C = $r for some n0.
159 :     #
160 :     return -1 unless $l->{adapt} == $r;
161 :     #
162 :     # Check that n0 is non-zero (i.e., there is a multiple of C in the student answer)
163 :     # (remember: return value of 0 is equal, and non-zero is unequal)
164 :     #
165 :     return abs($context->variables->get("n0")->{value}) < $context->flag("zeroLevelTol");
166 :     }
167 :    
168 : dpvc 5440 ##################################################
169 : dpvc 5393 #
170 : dpvc 5440 # Here we override part of the answer comparison
171 :     # routines in order to be able to generate
172 :     # helpful error messages for students when
173 :     # they leave off the + C.
174 :     #
175 :    
176 :     #
177 : dpvc 5393 # Show hints by default
178 :     #
179 : dpvc 5440 sub cmp_defaults {((shift)->SUPER::cmp_defaults,showHints => 1, showLinearityHints => 1)};
180 : dpvc 5393
181 :     #
182 :     # Add useful messages, if the author requested them
183 :     #
184 :     sub cmp_postprocess {
185 :     my $self = shift; my $ans = shift;
186 :     $self->SUPER::cmp_postprocess($ans);
187 :     return unless $ans->{score} == 0 && !$ans->{isPreview};
188 :     return if $ans->{ans_message} || !$self->getFlag("showHints");
189 : dpvc 5440 my $student = $ans->{student_value};
190 :     my $result = $ans->{correct_value} <=> $student; # compare encodes the reason in the result
191 : dpvc 5393 $self->cmp_Error($ans,"Note: there is always more than one posibility") if $result == 2 || $result == 3;
192 :     $self->cmp_Error($ans,"Your answer is not the most general solution")
193 : dpvc 5440 if $result == 1 || ($result == 3 && $self->removeConstant == $student);
194 :     $self->cmp_Error($ans,"Your formula should be linear in the constant '$student->{constant}'")
195 :     if $result == -1 && $self->getFlag("showLinearityHints") && !$student->D($student->{constant})->isConstant;
196 : dpvc 5393 }
197 :    
198 : dpvc 5440 ##################################################
199 : dpvc 5393 #
200 :     # Get the name of the constant
201 :     #
202 :     sub constant {(shift)->{constant}}
203 :    
204 :     #
205 :     # Remove the constant and return a Formula object
206 :     #
207 :     sub removeConstant {
208 :     my $self = shift;
209 :     main::Formula($self->substitute($self->{constant}=>0))->reduce;
210 :     }
211 :    
212 :     #
213 :     # Override the differentiation so that we always return
214 :     # a Formula, not a FormulaUpToConstant (we don't want to
215 :     # add the C in again).
216 :     #
217 :     sub D {
218 :     my $self = shift;
219 :     $self->removeConstant->D(@_);
220 :     }
221 :    
222 :     ######################################################################
223 :     #
224 :     # This class repalces the Parser::Variable class, and its job
225 :     # is to look for new constants that aren't in the context,
226 :     # and add them in. This allows students to use ANY constant
227 :     # they want, and a different one from the professor. We check
228 :     # that the student only used ONE arbitrary constant, however.
229 :     #
230 :     package FormulaUpToConstant::Variable;
231 :     our @ISA = ('Parser::Variable');
232 :    
233 :     sub new {
234 :     my $self = shift; my $class = ref($self) || $self;
235 :     my $equation = shift; my $variables = $equation->{context}{variables};
236 :     my ($name,$ref) = @_; my $def = $variables->{$name};
237 :     #
238 :     # If the variable is not already in the context, add it
239 :     # and mark it as an arbitrary constant (for later reference)
240 :     #
241 :     if (!defined($def) && length($name) eq 1) {
242 :     $equation->{context}->variables->add($name => 'Real');
243 :     $equation->{context}->variables->set($name => {arbitraryConstant => 1});
244 :     $def = $variables->{$name};
245 :     }
246 :     #
247 :     # If the variable is an arbitrary constant
248 :     # Error if we already have a constant and it's not this one.
249 :     # Save the constant so we can check with it later.
250 :     #
251 :     if ($def && $def->{arbitraryConstant}) {
252 :     $equation->Error(["Your formula shouldn't have two arbitrary constants"],$ref)
253 :     if $equation->{constant} and $name ne $equation->{constant};
254 :     $equation->{constant} = $name;
255 :     }
256 :     #
257 :     # Do the usual Variable stuff.
258 :     #
259 :     $self->SUPER::new($equation,$name,$ref);
260 :     }
261 :    
262 :     1;

aubreyja at gmail dot com
ViewVC Help
Powered by ViewVC 1.0.9