| … | |
… | |
| 463 | $rh_ans->{ra_student_ans} = \@answers; |
463 | $rh_ans->{ra_student_ans} = \@answers; |
| 464 | $rh_ans->{student_ans} = $display_ans unless $rh_ans->{error_flag}; |
464 | $rh_ans->{student_ans} = $display_ans unless $rh_ans->{error_flag}; |
| 465 | $rh_ans; |
465 | $rh_ans; |
| 466 | } |
466 | } |
| 467 | |
467 | |
|
|
468 | =head5 |
|
|
469 | This filter was created to get, format, and evaluate each entry of the ans_array and ans_array_extension |
|
|
470 | answer entry methods. Running this filter is necessary to get all the entries out of the answer |
|
|
471 | hash. Each entry is evaluated and the resulting number is put in the display for student answer |
|
|
472 | as a string. For evaluation purposes an array of arrays of arrays is created called ra_student_ans |
|
|
473 | and placed in the hash. The entries are [array_number][row_number][column_number]. The latex strings |
|
|
474 | for each entry are taken from the parser and put, as a matrix, into the previewer. The preview text |
|
|
475 | string is also created, but this display method becomes confusing when large matrices are used. |
|
|
476 | =cut |
|
|
477 | |
|
|
478 | |
| 468 | sub ans_array_filter{ |
479 | sub ans_array_filter{ |
| 469 | my $rh_ans = shift; |
480 | my $rh_ans = shift; |
| 470 | my %options = @_; |
481 | my %options = @_; |
| 471 | $rh_ans->{ans_label} =~ /ArRaY(\d+)\[\d+,\d+,\d+\]/; |
482 | $rh_ans->{ans_label} =~ /ArRaY(\d+)\[\d+,\d+,\d+\]/; |
| 472 | my $ans_num = $1; |
483 | my $ans_num = $1; |
| … | |
… | |
| 657 | |
668 | |
| 658 | mbox(\@temp); |
669 | mbox(\@temp); |
| 659 | |
670 | |
| 660 | } |
671 | } |
| 661 | |
672 | |
|
|
673 | sub vec_solution_cmp{ |
|
|
674 | my $correctAnswer = shift; |
|
|
675 | my %opt = @_; |
|
|
676 | |
|
|
677 | set_default_options( \%opt, |
|
|
678 | 'zeroLevelTol' => $main::functZeroLevelTolDefault, |
|
|
679 | 'debug' => 0, |
|
|
680 | 'mode' => 'basis', |
|
|
681 | 'help' => 'none', |
|
|
682 | ); |
|
|
683 | |
|
|
684 | $opt{debug} = 0 unless defined($opt{debug}); |
|
|
685 | |
|
|
686 | ## This is where the correct answer should be checked someday. |
|
|
687 | my $matrix = Matrix->new_from_col_vecs($correctAnswer); |
|
|
688 | |
|
|
689 | |
|
|
690 | #construct the answer evaluator |
|
|
691 | my $answer_evaluator = new AnswerEvaluator; |
|
|
692 | |
|
|
693 | $answer_evaluator->{debug} = $opt{debug}; |
|
|
694 | $answer_evaluator->ans_hash( correct_ans => display_correct_vecs($correctAnswer), |
|
|
695 | old_correct_ans => $correctAnswer, |
|
|
696 | rm_correct_ans => $matrix, |
|
|
697 | zeroLevelTol => $opt{zeroLevelTol}, |
|
|
698 | debug => $opt{debug}, |
|
|
699 | mode => $opt{mode}, |
|
|
700 | help => $opt{help}, |
|
|
701 | ); |
|
|
702 | |
|
|
703 | $answer_evaluator->install_pre_filter(\&ans_array_filter); |
|
|
704 | $answer_evaluator->install_pre_filter(sub{ |
|
|
705 | my ($rh_ans,@options) = @_; |
|
|
706 | my @student_array = @{$rh_ans->{ra_student_ans}}; |
|
|
707 | my @array = (); |
|
|
708 | for( my $i = 0; $i < scalar(@student_array) ; $i ++ ) |
|
|
709 | { |
|
|
710 | push( @array, Matrix->new_from_array_ref($student_array[$i])); |
|
|
711 | } |
|
|
712 | $rh_ans->{ra_student_ans} = \@array; |
|
|
713 | $rh_ans; |
|
|
714 | });#ra_student_ans is now the students answer as an array of vectors |
|
|
715 | # anonymous subroutine to check dimension and length of the student vectors |
|
|
716 | # if either is wrong, the answer is wrong. |
|
|
717 | $answer_evaluator->install_pre_filter(sub{ |
|
|
718 | my $rh_ans = shift; |
|
|
719 | my $length = $rh_ans->{rm_correct_ans}->[1]; |
|
|
720 | my $dim = $rh_ans->{rm_correct_ans}->[2]; |
|
|
721 | if( $dim != scalar(@{$rh_ans->{ra_student_ans}})) |
|
|
722 | { |
|
|
723 | |
|
|
724 | $rh_ans->{score} = 0; |
|
|
725 | if( $rh_ans->{help} =~ /dim|verbose/ ) |
|
|
726 | { |
|
|
727 | $rh_ans->throw_error('EVAL','You have entered the wrong number of vectors.'); |
|
|
728 | }else{ |
|
|
729 | $rh_ans->throw_error('EVAL'); |
|
|
730 | } |
|
|
731 | } |
|
|
732 | for( my $i = 0; $i < scalar( @{$rh_ans->{ra_student_ans} }) ; $i++ ) |
|
|
733 | { |
|
|
734 | if( $length != $rh_ans->{ra_student_ans}->[$i]->[1]) |
|
|
735 | { |
|
|
736 | $rh_ans->{score} = 0; |
|
|
737 | if( $rh_ans->{help} =~ /length|verbose/ ) |
|
|
738 | { |
|
|
739 | $rh_ans->throw_error('EVAL','You have entered vector(s) of the wrong length.'); |
|
|
740 | }else{ |
|
|
741 | $rh_ans->throw_error('EVAL'); |
|
|
742 | } |
|
|
743 | } |
|
|
744 | } |
|
|
745 | $rh_ans; |
|
|
746 | }); |
|
|
747 | # Install prefilter for various modes |
|
|
748 | if( $opt{mode} ne 'basis' ) |
|
|
749 | { |
|
|
750 | if( $opt{mode} =~ /orthogonal|orthonormal/ ) |
|
|
751 | { |
|
|
752 | $answer_evaluator->install_pre_filter(\&are_orthogonal_vecs); |
|
|
753 | } |
|
|
754 | |
|
|
755 | if( $opt{mode} =~ /unit|orthonormal/ ) |
|
|
756 | { |
|
|
757 | $answer_evaluator->install_pre_filter(\&are_unit_vecs); |
|
|
758 | |
|
|
759 | } |
|
|
760 | } |
|
|
761 | |
|
|
762 | $answer_evaluator->install_evaluator(\&compare_vec_solution, %opt); |
|
|
763 | |
|
|
764 | $answer_evaluator->install_post_filter( |
|
|
765 | sub {my $rh_ans = shift; |
|
|
766 | if ($rh_ans->catch_error('SYNTAX') ) { |
|
|
767 | $rh_ans->{ans_message} = $rh_ans->{error_message}; |
|
|
768 | $rh_ans->clear_error('SYNTAX'); |
|
|
769 | } |
|
|
770 | if ($rh_ans->catch_error('EVAL') ) { |
|
|
771 | $rh_ans->{ans_message} = $rh_ans->{error_message}; |
|
|
772 | $rh_ans->clear_error('EVAL'); |
|
|
773 | } |
|
|
774 | $rh_ans; |
|
|
775 | } |
|
|
776 | ); |
|
|
777 | $answer_evaluator; |
|
|
778 | |
|
|
779 | } |
|
|
780 | |
|
|
781 | |
|
|
782 | sub compare_vec_solution { |
|
|
783 | my ( $rh_ans, %options ) = @_ ; |
|
|
784 | my @space = @{$rh_ans->{ra_student_ans}}; |
|
|
785 | my $solution = shift @space; |
|
|
786 | |
|
|
787 | # A lot of the follosing code was taken from Matrix::proj_coeff |
|
|
788 | # calling this method recursively would be a waste of time since |
|
|
789 | # the prof's matrix never changes and solve_LR is an expensive |
|
|
790 | # operation. This way it is only done once. |
|
|
791 | my $matrix = $rh_ans->{rm_correct_ans}; |
|
|
792 | my ($dim,$x_vector, $base_matrix); |
|
|
793 | my $errors = undef; |
|
|
794 | my $lin_space_tr= ~ $matrix; |
|
|
795 | $matrix = $lin_space_tr * $matrix; |
|
|
796 | my $matrix_lr = $matrix->decompose_LR(); |
|
|
797 | |
|
|
798 | #this section determines whether or not the first vector, a solution to |
|
|
799 | #the system, is a linear combination of the prof's vectors in which there |
|
|
800 | #is a nonzero coefficient on the first term, the prof's solution to the system |
|
|
801 | $solution = $lin_space_tr*$solution; |
|
|
802 | ($dim,$x_vector, $base_matrix) = $matrix_lr->solve_LR($solution); |
|
|
803 | if( $dim ){ |
|
|
804 | $rh_ans->throw_error('EVAL', "A unique adapted answer could not be determined. Possibly the parameters have coefficient zero.<br> dim = $dim base_matrix is $base_matrix\n" ); # only print if the dim is not zero. |
|
|
805 | $rh_ans->{score} = 0; |
|
|
806 | $rh_ans; |
|
|
807 | }elsif( abs($x_vector->[0][0][0]) <= $options{zeroLevelTol} ) |
|
|
808 | { |
|
|
809 | $rh_ans->{score} = 0; |
|
|
810 | $rh_ans; |
|
|
811 | }else{ |
|
|
812 | $rh_ans->{score} = 1; |
|
|
813 | my @correct_space = @{$rh_ans->{old_correct_ans}}; |
|
|
814 | shift @correct_space; |
|
|
815 | $rh_ans->{rm_correct_ans} = Matrix->new_from_col_vecs(\@correct_space); |
|
|
816 | $rh_ans->{ra_student_ans} = \@space; |
|
|
817 | return compare_basis( $rh_ans, %options ); |
|
|
818 | } |
|
|
819 | } |
|
|
820 | |
|
|
821 | |
| 662 | 1; |
822 | 1; |