// Numbas version: exam_results_page_options {"name": "Differential Equation Question Type", "extensions": ["Differentiation"], "custom_part_types": [{"source": {"pk": 29, "author": {"name": "Andrew Iskauskas", "pk": 724}, "edit_page": "/part_type/29/edit"}, "name": "Differential Equation", "short_name": "differential-equation", "description": "

For questions on differentiation. Takes the student answer and explicitly checks that it satisfies a given differential equation.

\n

Will also check that the general solution has the correct number of linearly independent solutions (for up to a third-order ODE).

", "help_url": "", "input_widget": "jme", "input_options": {"correctAnswer": "settings['sample']", "hint": {"static": true, "value": ""}, "showPreview": {"static": true, "value": true}}, "can_be_gap": true, "can_be_step": true, "marking_script": "mark:\napply(unexpectedVariables);\napply(sameVars);\napply(hasConstants);\nif(length(constants)=2 or length(constants)=3, apply(notInd), true);\napply(numericallyCorrect)\n\ninterpreted_answer:\nstring(studentAnswer)\n\nequation:\njoin(map(settings['decoeffs'][n]+\"*(\"+d(string(studentAnswer),settings['arg'],length(settings['decoeffs'])-1-n)+\")\",n,0..length(settings['decoeffs'])-1),'+')\n\nagree:\nmap(\n try(\n resultsEqual(eval(parse(equation),vars),eval(settings['RHS'],vars),'RelDiff',0.0001),\n message,\n warn(translate(\"part.jme.answer invalid\",[\"message\":message]));\n fail(translate(\"part.jme.answer invalid\",[\"message\":message]));\n false\n ),\n vars,\n vset\n )\n\nvset:\nrepeat(\n dict(map([x,random(vRange)],x,correctVars or studentVariables)),\n 5\n)\n\ncorrectVars:\nset(settings['arg'])\n\nstudentVariables:\nset(findvars(studentAnswer))\n\nvRange:\n0..1#0\n\nunexpectedVariables:\nif (settings['var'] in list(studentVariables),\n incorrect(\"$\"+settings['var']+\"$ is the function $\"+settings['var']+\"(\"+settings['arg']+\")$; it should not be in the final answer.\"); end(),\n false\n ) \n\nsameVars:\nif (not (settings['arg'] in list(studentVariables)),\n incorrect(\"The argument of the function is $\"+settings['arg']+\"$; your solution does not contain it.\"); end(),\n true\n )\n\nnumFails:\napply(agree);\nlen(filter(not x,x,agree))\n\nnumericallyCorrect:\napply(numFails);\nif(numFails<1,\n correct(translate('part.jme.marking.correct')),\n incorrect()\n ) \n\nhasConstants:\nif(not(length(studentVariables)=length(findvars(settings[\"sample\"]))),\n incorrect(\"Your solution does not have the required number of constants to be a general solution.\"); end(),\n true\n )\n\nconstants:\nfilter(not(x=settings['arg']),x,map(x,x,studentVariables))\n\ntestSet:\nrepeat([settings['arg'],random(vRange)],5)\n\ntestingSets:\nmap(\nmap(\n dict(\n map([constants[m],if(m=n,1,0)],m,0..length(constants)-1)\n +[vars]),\n n,0..length(constants)-1\n),\nvars,testSet)\n\nlinears:\nmap(\nmap(\n map(eval(parse(d(string(studentAnswer),settings['arg'],m)),vars),m,0..length(constants)-1),\n vars,testingSets[x0]),\nx0,0..length(testingSets)-1)\n\nnotInd:\nif(length(constants)=2 or length(constants)=3,\nif(some(map(det(matrix(linears[n]))=0,n,0..length(linears)-1)),\n incorrect(\"Your solutions are not linearly independent.\"); end(),\n false\n),\nfalse\n)", "marking_notes": [{"description": "This is the main marking note. It should award credit and provide feedback based on the student's answer.", "definition": "apply(unexpectedVariables);\napply(sameVars);\napply(hasConstants);\nif(length(constants)=2 or length(constants)=3, apply(notInd), true);\napply(numericallyCorrect)", "name": "mark"}, {"description": "A value representing the student's answer to this part.", "definition": "string(studentAnswer)", "name": "interpreted_answer"}, {"description": "

The differential equation in the question

", "definition": "join(map(settings['decoeffs'][n]+\"*(\"+d(string(studentAnswer),settings['arg'],length(settings['decoeffs'])-1-n)+\")\",n,0..length(settings['decoeffs'])-1),'+')", "name": "equation"}, {"description": "

Do the student's answer and the expected answer agree on each of the sets of variable values?

", "definition": "map(\n try(\n resultsEqual(eval(parse(equation),vars),eval(settings['RHS'],vars),'RelDiff',0.0001),\n message,\n warn(translate(\"part.jme.answer invalid\",[\"message\":message]));\n fail(translate(\"part.jme.answer invalid\",[\"message\":message]));\n false\n ),\n vars,\n vset\n )", "name": "agree"}, {"description": "

The set of variable values to test against

", "definition": "repeat(\n dict(map([x,random(vRange)],x,correctVars or studentVariables)),\n 5\n)", "name": "vset"}, {"description": "

Variables used in the correct answer

", "definition": "set(settings['arg'])", "name": "correctVars"}, {"description": "

Variables used in the student's answer

", "definition": "set(findvars(studentAnswer))", "name": "studentVariables"}, {"description": "

The range to pick variable values from

", "definition": "0..1#0", "name": "vRange"}, {"description": "

Unexpected variables used in the student's answer

", "definition": "if (settings['var'] in list(studentVariables),\n incorrect(\"$\"+settings['var']+\"$ is the function $\"+settings['var']+\"(\"+settings['arg']+\")$; it should not be in the final answer.\"); end(),\n false\n ) ", "name": "unexpectedVariables"}, {"description": "

Does the student use the same variables as the correct answer?

", "definition": "if (not (settings['arg'] in list(studentVariables)),\n incorrect(\"The argument of the function is $\"+settings['arg']+\"$; your solution does not contain it.\"); end(),\n true\n )", "name": "sameVars"}, {"description": "

The number of times the student's answer and the expected answer disagree

", "definition": "apply(agree);\nlen(filter(not x,x,agree))", "name": "numFails"}, {"description": "

Is the student's answer numerically correct?

", "definition": "apply(numFails);\nif(numFails<1,\n correct(translate('part.jme.marking.correct')),\n incorrect()\n ) ", "name": "numericallyCorrect"}, {"description": "

Checks that the student's solution has the correct number of undetermined constants, by comparing to the sample solution.

", "definition": "if(not(length(studentVariables)=length(findvars(settings[\"sample\"]))),\n incorrect(\"Your solution does not have the required number of constants to be a general solution.\"); end(),\n true\n )", "name": "hasConstants"}, {"description": "

Finds the constants in the student's expression.

", "definition": "filter(not(x=settings['arg']),x,map(x,x,studentVariables))", "name": "constants"}, {"description": "

Generates a random set of x values to test independence of solutions over.

", "definition": "repeat([settings['arg'],random(vRange)],5)", "name": "testSet"}, {"description": "

Creates a set of 'linearly independent' solutions, by sequentially setting one constant to one and the others to zero.

", "definition": "map(\nmap(\n dict(\n map([constants[m],if(m=n,1,0)],m,0..length(constants)-1)\n +[vars]),\n n,0..length(constants)-1\n),\nvars,testSet)", "name": "testingSets"}, {"description": "

Evaluates the 'linearly independent' solutions over a pre-determined sample of argument values.

", "definition": "map(\nmap(\n map(eval(parse(d(string(studentAnswer),settings['arg'],m)),vars),m,0..length(constants)-1),\n vars,testingSets[x0]),\nx0,0..length(testingSets)-1)", "name": "linears"}, {"description": "

Checks that the student does have the right number of linearly independent solutions. Fails if any of the determinants are zero: the determinants are the Wronskian W for the 'independent' solutions y1(x)=y(x;1,0,...,0), y2(x)=(x;0,1,...,0),...,yn(x)=(x;0,0,...,1).

\n

", "definition": "if(length(constants)=2 or length(constants)=3,\nif(some(map(det(matrix(linears[n]))=0,n,0..length(linears)-1)),\n incorrect(\"Your solutions are not linearly independent.\"); end(),\n false\n),\nfalse\n)", "name": "notInd"}], "settings": [{"hint": "A possible solution, to be shown to the student on 'Reveal Answer'.", "label": "Sample Solution", "help_url": "", "subvars": true, "default_value": "", "name": "sample", "input_type": "mathematical_expression"}, {"hint": "A list of strings corresponding to the (possible function-valued) coefficients of the various terms in the differential equation.
The entries $1,0,4$ gives the differential equation $\\ddot{y}+4y=0$.", "label": "Differential Equation Coefficients", "help_url": "", "subvars": true, "default_value": [], "name": "decoeffs", "input_type": "list_of_strings"}, {"hint": "The right-hand side of the differential equation. Will be zero if the differential equation is homogeneous.", "label": "Right-hand Side", "help_url": "", "subvars": true, "default_value": "0", "name": "RHS", "input_type": "mathematical_expression"}, {"hint": "The argument with respect to whom the quantity is being differentiated.", "label": "Argument", "help_url": "", "subvars": false, "default_value": "t", "name": "arg", "input_type": "string"}, {"hint": "The function quantity being differentiated.", "label": "Variable", "help_url": "", "subvars": false, "default_value": "y", "name": "var", "input_type": "string"}], "public_availability": "always", "published": true, "extensions": ["Differentiation"]}], "resources": [], "navigation": {"allowregen": true, "showfrontpage": false, "preventleave": false, "typeendtoleave": false}, "question_groups": [{"pickingStrategy": "all-ordered", "questions": [{"name": "Differential Equation Question Type", "variable_groups": [], "ungrouped_variables": ["root1", "root2", "sol"], "advice": "

The characteristic equation is given by trying the solution $x(t)=e^{\\lambda t}$: here this gives

\n

$\\simplify{lambda^(2)*e^(lambda*t)+{-root1-root2}*lambda*e^(lambda t)+{root1*root2}*e^(lambda t)}=0.$

\n

Since $e^{\\lambda t}>0$ for all $t\\in(-\\infty,\\infty)$, to solve this we must have

\n

$\\simplify{lambda^(2)+{-root1-root2}lambda+{root1*root2}}=0$

\n
\n

which means that we have a repeated root, $\\lambda=\\var{root1}$. To have two linearly independent solutions, we must therefore take $x_{1}(t)=e^{\\var{root1}t}$ and $x_{2}(t)=te^{\\var{root1}t}$. Then the general solution is given by

\n

$x(t)=\\simplify{(A+B*t)*e^({root1}*t)},$

\n
\n
\n

which means that $\\lambda=\\var{root1}$ or $\\lambda=\\var{root2}$. This gives two linearly independent solutions: $x_{1}(t)=e^{\\var{root1}t}$ and $x_{2}(t)=e^{\\var{root2}t}$. So the general solution is

\n

$x(t)=\\simplify{A*e^({root1}*t)+B*e^({root2}*t)},$

\n
\n

where $A$ and $B$ are undetermined constants of integration.

", "variables": {"root1": {"definition": "random(-5..5)", "name": "root1", "description": "", "templateType": "anything", "group": "Ungrouped variables"}, "root2": {"definition": "random(-5..5)", "name": "root2", "description": "", "templateType": "anything", "group": "Ungrouped variables"}, "sol": {"definition": "if(root1=root2,\"(A+B*t)*e^(\"+root1+\"t)\",\"A*e^(\"+root1+\"t)+B*e^(\"+root2+\"t)\")", "name": "sol", "description": "", "templateType": "anything", "group": "Ungrouped variables"}}, "parts": [{"variableReplacements": [], "type": "differential-equation", "customMarkingAlgorithm": "", "prompt": "

Find the general solution to the differential equation for $x(t)$:

\n

$\\simplify{x''(t)+{-root1-root2}x'(t)+{root1*root2}*x(t)}=0$

", "showFeedbackIcon": true, "unitTests": [], "extendBaseMarkingAlgorithm": true, "showCorrectAnswer": true, "settings": {"decoeffs": ["1", "{-(root1+root2)}", "{root1*root2}"], "arg": "t", "sample": "{simplify(expression(sol),'all')}", "var": "x"}, "variableReplacementStrategy": "originalfirst", "scripts": {}, "marks": "2"}], "statement": "

This question concerns finding solutions to a second-order constant-coefficient ODE.

", "preamble": {"css": "", "js": ""}, "functions": {}, "tags": ["characteristic equation", "differential equation", "differentiation"], "rulesets": {}, "variablesTest": {"maxRuns": 100, "condition": "not (root1=0 and root2=0)"}, "extensions": ["Differentiation"], "metadata": {"description": "

Solving a second-order constant coefficient ODE. Uses the differentiation extension: https://github.com/Tandethsquire/Differentiation, and the Differential Equation custom part type, to differentiate a student answer and ensure it satisfies the equation.

", "licence": "Creative Commons Attribution-NonCommercial-NoDerivs 4.0 International"}, "type": "question", "contributors": [{"name": "Andrew Iskauskas", "profile_url": "https://numbas.mathcentre.ac.uk/accounts/profile/724/"}]}]}], "contributors": [{"name": "Andrew Iskauskas", "profile_url": "https://numbas.mathcentre.ac.uk/accounts/profile/724/"}]}