Initial Conditions#
For some problems, it is necessary for students to choose some configurations or initial conditions
that can vary from student to student; handling all possible values of these initial conditions
would require writing quite a few references. Instead, PyBryt offers the
InitialCondition
class to
represent a value that will be set when the student’s submission is executed for use in writing
annotations.
Writing a Reference with Initial Conditions#
Using initial conditions in your references is pretty simple. Each
InitialCondition
has a name,
the first argument to its constructor. This name is how the value in the student submission will be
identified (but more on that later).
Once you’ve instantiated an
InitialCondition
, you can use it
as normal in value and other types of annotations (except attribute annotations).
ic = pybryt.InitialCondition("foo")
pybryt.Value(ic)
Sometimes, however, you may want to look for a value derived from an initial condition. For this
reason, InitialCondition
supports all of Python’s arithmetic operators, and you can also apply transformations by writing
functions:
pybryt.Value(ic + 2)
pybryt.Value(2 * ic - 3)
pybryt.Value(ic.apply(np.transpose).apply(np.linalg.norm))
pybryt.Value(ic.apply(lambda v: pow(v, 10, 73)))
Each statement inside the Value
constructors above
returns an InitialCondition
.
Initial conditions maintain a list of transformations that need to be applied to reach the final
value for the annotation, and when a value is supplied from the submission, the transformations
are applied in sequence to determine the value that the value annotations should look for.
Collecting Initial Conditions in Submissions#
In order to link the initial conditions in the reference implementation to the values in the
submission, you must call
pybryt.set_initial_conditions
in the
submission. This function accepts a dictionary as its only argument mapping strings corresponding
to the names of initial conditions to the values of those initial conditions. When PyBryt is not
actively tracing, it has no effect; but, when called while PyBryt executes the submission, it tells
PyBryt to store the initial conditions passed to it in the memory footprint for later use by the
annotations.
For example, continuing the example from above, you would need to call
pybryt.set_initial_conditions({
"foo": 2,
})
to set the initial condition.
This function can be called multiple times, but the memory footprint only retains a single store of
initial conditions, so calling it multiple times with the same key will overwrite any old values
of that key. (For example, calling it with {"foo": 1, "bar": 3}
and then {"foo": 2}
will result in initial conditions {"foo": 2, "bar": 3}
).