[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 5555 - (view) (download) (as text)

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

aubreyja at gmail dot com
ViewVC Help
Powered by ViewVC 1.0.9