QFLRA requirements¶
The backend for qflra requirements — quantifier-free linear real arithmetic
(linear inequalities combined with boolean operators such as or and neg).
Shield Layer¶
shield_layer ¶
The QFLRA Shield Layer, the main public entry point of this subpackage.
Defines :class:ShieldLayer, a differentiable PyTorch module that corrects neural
network predictions so they satisfy a set of QFLRA requirements.
ShieldLayer ¶
Bases: Module
Differentiable layer that corrects predictions so they satisfy a set of QFLRA requirements: quantifier-free linear real arithmetic, i.e. disjunctions ('or') and negations ('neg') of linear inequalities.
Like the linear ShieldLayer, the correction proceeds variable by variable following a fixed
ordering, clipping each variable into the feasible region implied by its constraints given the
already-corrected values of the preceding variables. The difference is that disjunctions make
that feasible region a union of intervals rather than a single interval, so the per-variable
correction (delegated to correct_preds) is more involved than in the purely conjunctive
linear case.
Attributes:
| Name | Type | Description |
|---|---|---|
num_variables |
The number of variables (prediction dimensions). |
|
ordering |
The ordering of variables used to drive the correction. |
|
constraints |
The parsed list of QFLRA :class: |
|
sets_of_constr |
Mapping from each :class: |
Example
layer = ShieldLayer(num_variables=2, requirements_filepath='constraints.txt') corrected = layer(preds) # preds is a (B, 2) tensor
Build the Shield Layer from a requirements file.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
num_variables
|
int
|
The number of variables (prediction dimensions). |
required |
requirements_filepath
|
str
|
Path to the file holding the variable ordering and the QFLRA constraints. |
required |
ordering_choice
|
str
|
How to order variables for correction; |
'given'
|
Source code in pishield/qflra_requirements/shield_layer.py
Parser¶
parser ¶
Parser for QFLRA requirements files.
Reads a requirements file declaring a variable ordering and a list of constraints
(disjunctions and negations of linear inequalities) and builds the corresponding
:class:Variable and :class:Constraint objects.
neg_postprocess_ineq ¶
Apply boolean negation to an inequality.
Negating a linear inequality is equivalent to negating both sides and flipping
the inequality sign, e.g. neg (x1+x2-x3 >= c) becomes -x1-x2+x3 > -c.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
ineq
|
Inequality
|
The :class: |
required |
Returns:
| Type | Description |
|---|---|
Inequality
|
A new negated :class: |
Source code in pishield/qflra_requirements/parser.py
parse_atom ¶
Parse a single signed coefficient-variable term into an :class:Atom.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x
|
The atom string, e.g. |
required |
Returns:
| Type | Description |
|---|---|
|
The parsed :class: |
Source code in pishield/qflra_requirements/parser.py
parse_inequality ¶
Parse a linear inequality string into an :class:Inequality.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
inequality
|
The inequality string, e.g. |
required |
Returns:
| Type | Description |
|---|---|
|
The parsed :class: |
Source code in pishield/qflra_requirements/parser.py
parse_constraint ¶
Parse a constraint line into a :class:Constraint.
The line may contain several inequalities joined by 'or' (forming a
disjunction); a clause prefixed with 'neg' is negated after parsing.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
constr
|
The constraint string, e.g. |
required |
Returns:
| Type | Description |
|---|---|
Constraint
|
The parsed :class: |
Source code in pishield/qflra_requirements/parser.py
parse_constraints_file ¶
Parse a requirements file into a variable ordering and constraints.
The file must contain a line starting with 'ordering' listing the variables,
plus one constraint per remaining line.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
file
|
str
|
Path to the requirements file. |
required |
Returns:
| Type | Description |
|---|---|
(List[Variable], List[Constraint])
|
A tuple |
(List[Variable], List[Constraint])
|
and the list of parsed :class: |
Raises:
| Type | Description |
|---|---|
Exception
|
If no ordering line is found in the file. |
Source code in pishield/qflra_requirements/parser.py
main ¶
Run a small demo: parse a constraints file and print the constraints.
Source code in pishield/qflra_requirements/parser.py
Classes¶
classes ¶
Core data structures for QFLRA requirements.
Defines variables, linear inequality atoms, inequalities, disjunctions of inequalities and constraints, together with methods to inspect, render and evaluate them on batched predictions.
Variable ¶
A variable (label or feature) identified by a string name.
Attributes:
| Name | Type | Description |
|---|---|---|
variable |
The string name of the variable, e.g. |
|
id |
The integer id parsed from the trailing part of the name. |
Initialise the variable from its string name.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
variable
|
str
|
The variable name, whose trailing integer (after the last
|
required |
Source code in pishield/qflra_requirements/classes.py
Atom ¶
A signed linear term (coefficient times a variable) in an inequality body.
Attributes:
| Name | Type | Description |
|---|---|---|
variable |
The :class: |
|
coefficient |
The absolute value of the coefficient. |
|
positive_sign |
Whether the term is added ( |
Initialise the atom.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
variable
|
Variable
|
The :class: |
required |
coefficient
|
float
|
The coefficient; its absolute value is stored. |
required |
positive_sign
|
bool
|
|
required |
Source code in pishield/qflra_requirements/classes.py
get_variable_id ¶
Return the id of this atom's variable.
Returns:
| Type | Description |
|---|---|
|
The integer id of the underlying :class: |
eval ¶
Evaluate the atom at a given variable value.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x_value
|
The value (possibly batched tensor) of the variable. |
required |
Returns:
| Type | Description |
|---|---|
|
|
Source code in pishield/qflra_requirements/classes.py
get_signed_coefficient ¶
Return the coefficient with its sign applied.
Returns:
| Type | Description |
|---|---|
|
|
Source code in pishield/qflra_requirements/classes.py
readable ¶
Return a human-readable string for the atom, e.g. ' + 2y_3'.
Returns:
| Type | Description |
|---|---|
|
The signed, formatted term as a string. |
Source code in pishield/qflra_requirements/classes.py
get_atom_attributes ¶
Return the atom's defining attributes.
Returns:
| Type | Description |
|---|---|
|
A tuple |
Inequality ¶
A single linear inequality body <sign> constant.
Attributes:
| Name | Type | Description |
|---|---|---|
ineq_sign |
The inequality sign, either |
|
constant |
The right-hand side constant. |
|
body |
List of :class: |
Initialise the inequality.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
body
|
List[Atom]
|
List of :class: |
required |
ineq_sign
|
str
|
Either |
required |
constant
|
float
|
The right-hand side constant. |
required |
Raises:
| Type | Description |
|---|---|
AssertionError
|
If |
Source code in pishield/qflra_requirements/classes.py
readable ¶
Return a human-readable string for the inequality.
Returns:
| Type | Description |
|---|---|
|
The rendered inequality, e.g. |
Source code in pishield/qflra_requirements/classes.py
get_body_variables ¶
Return the variables appearing in the inequality body.
Returns:
| Type | Description |
|---|---|
|
A list of :class: |
Source code in pishield/qflra_requirements/classes.py
get_body_atoms ¶
Return the atoms of the inequality body.
Returns:
| Type | Description |
|---|---|
|
A list of the body :class: |
get_x_complement_body_atoms ¶
Split the body into the atom of x and the remaining atoms.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x
|
Variable
|
The variable to isolate. |
required |
Returns:
| Type | Description |
|---|---|
(List[Atom], Atom, bool)
|
A tuple |
(List[Atom], Atom, bool)
|
|
(List[Atom], Atom, bool)
|
|
(List[Atom], Atom, bool)
|
|
(List[Atom], Atom, bool)
|
for a |
Raises:
| Type | Description |
|---|---|
AssertionError
|
If |
Source code in pishield/qflra_requirements/classes.py
get_ineq_attributes ¶
Return the inequality's defining attributes.
Returns:
| Type | Description |
|---|---|
|
A tuple |
contains_variable ¶
Report whether the variable appears anywhere in the body.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x
|
Variable
|
The variable to look for. |
required |
Returns:
| Type | Description |
|---|---|
|
|
Source code in pishield/qflra_requirements/classes.py
contains_variable_only_positively ¶
Report whether the variable appears with a positive sign.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x
|
Variable
|
The variable to look for. |
required |
Returns:
| Type | Description |
|---|---|
|
|
Source code in pishield/qflra_requirements/classes.py
contains_variable_only_negatively ¶
Report whether the variable appears with a negative sign.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x
|
Variable
|
The variable to look for. |
required |
Returns:
| Type | Description |
|---|---|
|
|
Source code in pishield/qflra_requirements/classes.py
contains_variable_both_positively_and_negatively ¶
Report mixed-sign occurrence of a variable.
A single inequality never contains a variable both positively and negatively; this is only possible for a disjunctive inequality.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x
|
Variable
|
The variable to look for (unused). |
required |
Returns:
| Type | Description |
|---|---|
|
Always |
Source code in pishield/qflra_requirements/classes.py
get_ineq_with_pos_and_neg_var_y_and_complement ¶
Return positive/negative clauses of a variable (disjunction-only).
Only meaningful for a disjunctive inequality; a single inequality has no such split.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x
|
Variable
|
The variable to look for (unused). |
required |
Returns:
| Type | Description |
|---|---|
|
Always |
Source code in pishield/qflra_requirements/classes.py
get_ineq_with_var_y_and_complement ¶
Return this inequality if it contains the variable, else None.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x
|
Variable
|
The variable to look for. |
required |
Returns:
| Type | Description |
|---|---|
|
|
|
|
single inequality), otherwise |
Source code in pishield/qflra_requirements/classes.py
check_satisfaction ¶
Evaluate satisfaction of the inequality on batched predictions.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
preds
|
Tensor
|
Predictions tensor of shape |
required |
Returns:
| Type | Description |
|---|---|
Tensor
|
A boolean tensor of shape |
Tensor
|
satisfying the inequality (within |
Raises:
| Type | Description |
|---|---|
NotImplementedError
|
If the inequality sign is unsupported. |
Source code in pishield/qflra_requirements/classes.py
check_satisfaction_all ¶
Report whether all samples satisfy the inequality.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
preds
|
Tensor
|
Predictions tensor of shape |
required |
Returns:
| Type | Description |
|---|---|
bool
|
|
Source code in pishield/qflra_requirements/classes.py
eval_to_bool ¶
Reduce per-sample satisfaction to a single boolean.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
preds
|
Tensor
|
Predictions tensor of shape |
required |
Returns:
| Type | Description |
|---|---|
bool
|
|
Source code in pishield/qflra_requirements/classes.py
DisjunctInequality ¶
A disjunction ('or') of linear inequalities.
Attributes:
| Name | Type | Description |
|---|---|---|
list_inequalities |
The :class: |
Initialise the disjunction.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
inequality_list
|
List[Inequality]
|
The list of :class: |
required |
Source code in pishield/qflra_requirements/classes.py
readable ¶
Return a multi-line human-readable string listing each disjunct.
Returns:
| Type | Description |
|---|---|
|
A string with one |
Source code in pishield/qflra_requirements/classes.py
verbose_readable ¶
Return a single-line human-readable string joining clauses with 'or'.
Returns:
| Type | Description |
|---|---|
|
The clauses rendered and joined by |
Source code in pishield/qflra_requirements/classes.py
contains_variable ¶
Report whether the variable appears in any clause.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x
|
Variable
|
The variable to look for. |
required |
Returns:
| Type | Description |
|---|---|
|
|
Source code in pishield/qflra_requirements/classes.py
contains_variable_only_positively ¶
Report whether the variable occurs only positively across clauses.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x
|
Variable
|
The variable to look for. |
required |
Returns:
| Type | Description |
|---|---|
|
|
Source code in pishield/qflra_requirements/classes.py
contains_variable_only_negatively ¶
Report whether the variable occurs only negatively across clauses.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x
|
Variable
|
The variable to look for. |
required |
Returns:
| Type | Description |
|---|---|
|
|
Source code in pishield/qflra_requirements/classes.py
contains_variable_both_positively_and_negatively ¶
Report whether the variable occurs both positively and negatively.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x
|
Variable
|
The variable to look for. |
required |
Returns:
| Type | Description |
|---|---|
|
|
|
|
another. |
Source code in pishield/qflra_requirements/classes.py
get_ineq_with_var_y_and_complement ¶
Find the clause containing a variable and the remaining clauses.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x
|
Variable
|
The variable to look for. |
required |
Returns:
| Type | Description |
|---|---|
|
A tuple |
|
|
containing |
|
|
no clause contains |
Source code in pishield/qflra_requirements/classes.py
get_ineq_with_pos_and_neg_var_y_and_complement ¶
Find the positive-x and negative-x clauses and the remaining clauses.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x
|
Variable
|
The variable to look for. |
required |
Returns:
| Type | Description |
|---|---|
|
A tuple |
|
|
where |
|
|
and the other clauses. |
Raises:
| Type | Description |
|---|---|
NotImplementedError
|
If either a positive-x or negative-x clause is missing. |
Source code in pishield/qflra_requirements/classes.py
get_body_variables ¶
Return the variables across all clause bodies.
Returns:
| Type | Description |
|---|---|
|
A list of :class: |
Source code in pishield/qflra_requirements/classes.py
get_body_atoms ¶
Return the atoms across all clause bodies.
Returns:
| Type | Description |
|---|---|
|
A flat list of :class: |
Source code in pishield/qflra_requirements/classes.py
split_ineqs_with_and_without_x ¶
Partition the clauses by whether they contain a variable.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x
|
Variable
|
The variable to look for. |
required |
Returns:
| Type | Description |
|---|---|
(List[Inequality], List[Inequality])
|
A tuple |
Source code in pishield/qflra_requirements/classes.py
get_x_complement_body_atoms ¶
Select the clause with the requested sign of a variable and split it.
Picks the clause in which x occurs with sign_of_x (handling both the
mixed-sign and single-occurrence cases) and delegates to that inequality's
:meth:Inequality.get_x_complement_body_atoms.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x
|
Variable
|
The variable to isolate. |
required |
sign_of_x
|
str
|
Either |
required |
Returns:
| Type | Description |
|---|---|
(List[Atom], Atom)
|
The tuple returned by :meth: |
(List[Atom], Atom)
|
for the selected clause. |
Raises:
| Type | Description |
|---|---|
NotImplementedError
|
If |
Source code in pishield/qflra_requirements/classes.py
check_satisfaction ¶
Evaluate the disjunction on batched predictions.
A sample satisfies the disjunction if it satisfies at least one clause.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
preds
|
Tensor
|
Predictions tensor of shape |
required |
Returns:
| Type | Description |
|---|---|
Tensor
|
A boolean tensor of shape |
Tensor
|
satisfying any clause. |
Source code in pishield/qflra_requirements/classes.py
check_satisfaction_all ¶
Report whether all samples satisfy the disjunction.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
preds
|
Tensor
|
Predictions tensor of shape |
required |
Returns:
| Type | Description |
|---|---|
bool
|
|
Source code in pishield/qflra_requirements/classes.py
Constraint ¶
A single QFLRA requirement: one inequality or a disjunction of them.
Most methods delegate to the underlying inequality (or disjunction). When the
constraint holds a single inequality, disjunctive_inequality is that
:class:Inequality; otherwise it is a :class:DisjunctInequality.
Attributes:
| Name | Type | Description |
|---|---|---|
list_inequalities |
The inequalities making up the constraint. |
|
disjunctive_inequality |
The :class: |
Initialise the constraint from its inequalities.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
inequality_list
|
List[Inequality]
|
The :class: |
required |
Source code in pishield/qflra_requirements/classes.py
readable ¶
Return a human-readable string for the constraint.
Returns:
| Type | Description |
|---|---|
|
The rendered constraint. |
verbose_readable ¶
Return a single-line human-readable string for the constraint.
Returns:
| Type | Description |
|---|---|
|
The constraint rendered on one line (clauses joined with 'or'). |
Source code in pishield/qflra_requirements/classes.py
contains_variable ¶
Report whether the constraint involves a variable.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x
|
Variable
|
The variable to look for. |
required |
Returns:
| Type | Description |
|---|---|
|
|
Source code in pishield/qflra_requirements/classes.py
get_body_atoms ¶
Return the atoms of the constraint body.
Returns:
| Type | Description |
|---|---|
|
A list of :class: |
check_satisfaction ¶
Report whether all samples satisfy the constraint.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
preds
|
Predictions tensor of shape |
required |
Returns:
| Type | Description |
|---|---|
|
|
Source code in pishield/qflra_requirements/classes.py
contains_variable_only_positively ¶
Report whether the variable occurs only positively.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x
|
Variable
|
The variable to look for. |
required |
Returns:
| Type | Description |
|---|---|
|
|
Source code in pishield/qflra_requirements/classes.py
contains_variable_only_negatively ¶
Report whether the variable occurs only negatively.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x
|
Variable
|
The variable to look for. |
required |
Returns:
| Type | Description |
|---|---|
|
|
Source code in pishield/qflra_requirements/classes.py
contains_variable_both_positively_and_negatively ¶
Report whether the variable occurs both positively and negatively.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x
|
Variable
|
The variable to look for. |
required |
Returns:
| Type | Description |
|---|---|
|
|
Source code in pishield/qflra_requirements/classes.py
get_ineq_with_var_y_and_complement ¶
Return the inequality containing a variable and the remaining ones.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x
|
Variable
|
The variable to look for. |
required |
Returns:
| Type | Description |
|---|---|
|
The result of the underlying |
|
|
meth: |
Source code in pishield/qflra_requirements/classes.py
get_ineq_with_pos_and_neg_var_y_and_complement ¶
Return the positive-x and negative-x inequalities and the remainder.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x
|
Variable
|
The variable to look for. |
required |
Returns:
| Type | Description |
|---|---|
|
The result of the underlying |
|
|
meth: |
Source code in pishield/qflra_requirements/classes.py
Normalisation¶
normalisation ¶
Normalisation of QFLRA constraints with respect to a variable.
A constraint (a disjunction of linear inequalities) is put into a normal form in which a given variable occurs at most once positively and at most once negatively, by combining the inequalities where the variable appears with opposite signs.
normalise_constraint_wrt_selected_ineq ¶
normalise_constraint_wrt_selected_ineq(x: Variable, partially_normalised_inequality_list: List[Inequality], ineq_i: Inequality, signed_ineqs: list[Inequality], sign) -> List[Inequality]
Rewrite a set of inequalities relative to one selected inequality.
Given a selected inequality ineq_i in which variable x appears with the
given sign, every other inequality in signed_ineqs (where x appears
with the same sign) is rewritten by eliminating x against ineq_i. The
selected inequality is kept as-is and the rewritten inequalities are appended to
a copy of partially_normalised_inequality_list.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x
|
Variable
|
The variable being normalised away. |
required |
partially_normalised_inequality_list
|
List[Inequality]
|
Inequalities already accumulated for the new normalised constraint; copied, not mutated. |
required |
ineq_i
|
Inequality
|
The selected reference inequality containing |
required |
signed_ineqs
|
list[Inequality]
|
Inequalities in which |
required |
sign
|
Either |
required |
Returns:
| Type | Description |
|---|---|
List[Inequality]
|
The extended list of inequalities forming (part of) the normalised |
List[Inequality]
|
constraint. |
Raises:
| Type | Description |
|---|---|
NotImplementedError
|
If |
Source code in pishield/qflra_requirements/normalisation.py
normalise_one_constraint ¶
Normalise a single constraint with respect to a variable.
If x already occurs at most once positively and at most once negatively (or
not at all), the constraint needs no normalisation and is returned unchanged.
Otherwise the inequalities where x appears positively are paired against
those where it appears negatively, producing one or more normalised constraints.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x
|
Variable
|
The variable to normalise with respect to. |
required |
constraint
|
Constraint
|
The constraint (disjunction of inequalities) to normalise. |
required |
Returns:
| Type | Description |
|---|---|
Constraint | list[Constraint]
|
Either the original :class: |
Constraint | list[Constraint]
|
a list of normalised :class: |
Source code in pishield/qflra_requirements/normalisation.py
106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 | |
normalise ¶
Normalise every constraint in a list with respect to a variable.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x
|
Variable
|
The variable to normalise with respect to. |
required |
constraints
|
list[Constraint]
|
List of :class: |
required |
Returns:
| Type | Description |
|---|---|
list[Constraint]
|
A flat list of normalised :class: |
list[Constraint]
|
may expand into several after normalisation). |
Source code in pishield/qflra_requirements/normalisation.py
Computing sets of constraints¶
compute_sets_of_constraints ¶
Computation of per-variable constraint sets via variable elimination.
Following a variable ordering, this module derives, for each variable, the set of constraints that bound it once the higher-ranked variables have been eliminated by algebraic reduction (a resolution-style procedure over linear inequalities).
get_pos_pos_x_constr ¶
Derive constraints by reducing positive-y against mixed-sign-y constraints.
For each constraint where y appears only positively and each constraint where
y appears both positively and negatively, the positive inequality of the
former is reduced against the negative inequality of the latter to eliminate
y, building new constraints that retain the remaining inequalities.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
y
|
Variable
|
The variable being eliminated. |
required |
pos_constr
|
List[Constraint]
|
Constraints in which |
required |
pos_neg_constr
|
List[Constraint]
|
Constraints in which |
required |
Returns:
| Type | Description |
|---|---|
|
A list of derived :class: |
|
|
|
Source code in pishield/qflra_requirements/compute_sets_of_constraints.py
create_constr_by_reduction ¶
Eliminate a variable by reducing pairs of constraints over it.
The constraints containing y are split by the sign of y; pairs of
positive-y and negative-y constraints are then reduced to produce new
constraints that no longer contain y.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
y
|
Variable
|
The variable being eliminated. |
required |
constraints_with_y
|
List[Constraint]
|
Constraints that contain |
required |
Returns:
| Type | Description |
|---|---|
|
A list of derived :class: |
Source code in pishield/qflra_requirements/compute_sets_of_constraints.py
reduce_two_ineqs ¶
Combine two inequalities to eliminate a shared variable.
Scales and adds the two inequalities (one with y positive, one with y
negative) so that y cancels, yielding a new inequality on the remaining
atoms. When all atoms cancel, the result is the boolean truth value of the
resulting trivial inequality.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
y
|
The variable to eliminate. |
required | |
ineq_with_pos_y
|
An :class: |
required | |
ineq_with_neg_y
|
An :class: |
required |
Returns:
| Type | Description |
|---|---|
|
A new :class: |
|
|
satisfaction value when the reduced body is empty. |
Raises:
| Type | Description |
|---|---|
NotImplementedError
|
If the resulting inequality sign is unsupported. |
Source code in pishield/qflra_requirements/compute_sets_of_constraints.py
get_pos_neg_pn_x_constr ¶
Group constraints by how a variable occurs in them.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
y
|
Variable
|
The variable to inspect. |
required |
constraints
|
List[Constraint]
|
Constraints to classify. |
required |
Returns:
| Type | Description |
|---|---|
|
A tuple |
|
|
constraints where |
|
|
positively and negatively, respectively. |
Source code in pishield/qflra_requirements/compute_sets_of_constraints.py
get_pos_neg_x_constr ¶
Split constraints by the sign of a variable, discarding mixed-sign ones.
Constraints where y appears both positively and negatively are ignored.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
y
|
Variable
|
The variable to inspect. |
required |
constraints_with_y
|
List[Constraint]
|
Constraints that contain |
required |
Returns:
| Type | Description |
|---|---|
|
A tuple |
|
|
appears only positively and only negatively, respectively. |
Source code in pishield/qflra_requirements/compute_sets_of_constraints.py
compute_set_of_constraints_for_variable ¶
compute_set_of_constraints_for_variable(x: Variable, prev_x: Variable, normalised_constraints_at_previous_level: List[Constraint], verbose)
Compute the constraint set for the next variable in the ordering.
Constraints from the previous level that mention prev_x are reduced to
eliminate prev_x; the result is unioned with the constraints that did not
mention prev_x.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x
|
Variable
|
The variable for the current level (used for logging/context). |
required |
prev_x
|
Variable
|
The previously processed variable to be eliminated. |
required |
normalised_constraints_at_previous_level
|
List[Constraint]
|
Normalised constraints produced for
|
required |
verbose
|
Whether to print verbose progress information. |
required |
Returns:
| Type | Description |
|---|---|
|
The list of (still unnormalised) :class: |
|
|
contain |
Source code in pishield/qflra_requirements/compute_sets_of_constraints.py
compute_sets_of_constraints ¶
compute_sets_of_constraints(ordering: List[Variable], constraints: List[Constraint], verbose) -> {Variable: List[Constraint]}
Compute the normalised constraint set bounding each variable.
Processes the variables in reverse order, eliminating each higher-ranked variable in turn so that the constraints associated with a variable only involve that variable and lower-ranked ones. This is the precomputation consumed by the prediction-correction pass.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
ordering
|
List[Variable]
|
The variable ordering (ascending); processed in reverse. |
required |
constraints
|
List[Constraint]
|
The full list of parsed :class: |
required |
verbose
|
Whether to print verbose progress information. |
required |
Returns:
| Type | Description |
|---|---|
{Variable: List[Constraint]}
|
A dict mapping each :class: |
{Variable: List[Constraint]}
|
class: |
Source code in pishield/qflra_requirements/compute_sets_of_constraints.py
main ¶
Run a small demo: parse a constraints file and compute its constraint sets.
Source code in pishield/qflra_requirements/compute_sets_of_constraints.py
Correcting predictions¶
correct_predictions ¶
Differentiable correction of predictions against QFLRA requirements.
Given precomputed per-variable constraint sets, this module derives lower/upper bounds for each variable and clips predictions into the feasible region implied by the (possibly disjunctive) constraints, processing variables in the given ordering.
get_constr_at_level_x ¶
Look up the constraint set associated with a variable.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x
|
The variable whose constraints are requested. |
required | |
sets_of_constr
|
Mapping from :class: |
required |
Returns:
| Type | Description |
|---|---|
|
The list of :class: |
|
|
|
Source code in pishield/qflra_requirements/correct_predictions.py
get_lb_from_one_constraint ¶
Compute the per-sample lower bound on a variable from one constraint.
The constraint (in which x appears positively) is rearranged into the form
x >= ... and evaluated on the batch. A small epsilon is added for strict
inequalities.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x
|
Variable
|
The variable to bound. |
required |
constraint
|
Constraint
|
A constraint that bounds |
required |
preds
|
Tensor
|
Predictions tensor of shape |
required |
epsilon
|
Slack added for strict ('>') inequalities. |
1e-12
|
Returns:
| Type | Description |
|---|---|
|
A tensor of shape |
Source code in pishield/qflra_requirements/correct_predictions.py
get_ub_from_one_constraint ¶
Compute the per-sample upper bound on a variable from one constraint.
The constraint (in which x appears negatively) is rearranged into the form
x <= ... and evaluated on the batch. A small epsilon is subtracted for
strict inequalities.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x
|
Variable
|
The variable to bound. |
required |
constraint
|
Constraint
|
A constraint that bounds |
required |
preds
|
Tensor
|
Predictions tensor of shape |
required |
epsilon
|
Slack subtracted for strict ('>') inequalities. |
1e-12
|
Returns:
| Type | Description |
|---|---|
|
A tensor of shape |
|
|
|
Source code in pishield/qflra_requirements/correct_predictions.py
get_lower_bounds ¶
get_lower_bounds(x: Variable, x_constraints: List[Constraint], preds: Tensor, epsilon=1e-12) -> torch.Tensor
Collect the lower bounds on a variable from all relevant constraints.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x
|
Variable
|
The variable to bound. |
required |
x_constraints
|
List[Constraint]
|
Constraints that bound |
required |
preds
|
Tensor
|
Predictions tensor of shape |
required |
epsilon
|
Slack passed through to :func: |
1e-12
|
Returns:
| Type | Description |
|---|---|
Tensor
|
A tensor of shape |
Tensor
|
|
Source code in pishield/qflra_requirements/correct_predictions.py
get_upper_bounds ¶
get_upper_bounds(x: Variable, x_constraints: List[Constraint], preds: Tensor, epsilon=1e-12) -> torch.Tensor
Collect the upper bounds on a variable from all relevant constraints.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x
|
Variable
|
The variable to bound. |
required |
x_constraints
|
List[Constraint]
|
Constraints that bound |
required |
preds
|
Tensor
|
Predictions tensor of shape |
required |
epsilon
|
Slack passed through to :func: |
1e-12
|
Returns:
| Type | Description |
|---|---|
Tensor
|
A tensor of shape |
Tensor
|
|
Source code in pishield/qflra_requirements/correct_predictions.py
get_final_x_correction ¶
get_final_x_correction(initial_x_val: Tensor, lower_bounds: Tensor, upper_bounds: Tensor) -> torch.Tensor
Clip a variable's values into the feasible interval given its bounds.
The initial value is raised to the lower bound and then lowered to the upper bound (per sample). Infinite bounds are treated as no constraint. Used for the purely conjunctive (non-disjunctive) case.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
initial_x_val
|
Tensor
|
Tensor of shape |
required |
lower_bounds
|
Tensor
|
Greatest lower bound per sample, or a non-tensor sentinel when there is no lower bound. |
required |
upper_bounds
|
Tensor
|
Least upper bound per sample, or a non-tensor sentinel when there is no upper bound. |
required |
Returns:
| Type | Description |
|---|---|
Tensor
|
A tensor of shape |
Source code in pishield/qflra_requirements/correct_predictions.py
get_right_bounds ¶
get_right_bounds(x: Variable, partially_corrected_preds: Tensor, constraints_at_level_x: List[Constraint]) -> torch.Tensor
Compute the right (lower) bounds on a variable.
Right bounds are the lower bounds on x (values x must lie to the right
of), obtained from constraints where x appears positively.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x
|
Variable
|
The variable to bound. |
required |
partially_corrected_preds
|
Tensor
|
Predictions tensor of shape |
required |
constraints_at_level_x
|
List[Constraint]
|
Constraints bounding |
required |
Returns:
| Type | Description |
|---|---|
Tensor
|
The lower bounds tensor (or sentinel) returned by :func: |
Source code in pishield/qflra_requirements/correct_predictions.py
get_left_bounds ¶
get_left_bounds(x: Variable, partially_corrected_preds: Tensor, constraints_at_level_x: List[Constraint]) -> torch.Tensor
Compute the left (upper) bounds on a variable.
Left bounds are the upper bounds on x (values x must lie to the left
of), obtained from constraints where x appears negatively.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x
|
Variable
|
The variable to bound. |
required |
partially_corrected_preds
|
Tensor
|
Predictions tensor of shape |
required |
constraints_at_level_x
|
List[Constraint]
|
Constraints bounding |
required |
Returns:
| Type | Description |
|---|---|
Tensor
|
The upper bounds tensor (or sentinel) returned by :func: |
Source code in pishield/qflra_requirements/correct_predictions.py
get_closest_left_bound ¶
get_closest_left_bound(x, partially_corrected_preds_for_x, left_bounds, right_bounds, constraints_at_level_x)
Pick the feasible left (upper) bound closest to a variable's value.
Among the candidate left bounds, those that would still violate some constraint
are discarded; of the remaining ones, the bound closest to the current value of
x is returned (per sample).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x
|
The variable to bound. |
required | |
partially_corrected_preds_for_x
|
Predictions tensor of shape |
required | |
left_bounds
|
Candidate left bounds tensor of shape |
required | |
right_bounds
|
Unused in this routine (kept for signature symmetry). |
required | |
constraints_at_level_x
|
Constraints used to test feasibility of each bound. |
required |
Returns:
| Type | Description |
|---|---|
|
A tensor of shape |
|
|
or the |
Source code in pishield/qflra_requirements/correct_predictions.py
get_closest_right_bound ¶
get_closest_right_bound(x, partially_corrected_preds_for_x, left_bounds, right_bounds, constraints_at_level_x)
Pick the feasible right (lower) bound closest to a variable's value.
Among the candidate right bounds, those that would still violate some constraint
are discarded; of the remaining ones, the bound closest to the current value of
x is returned (per sample).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x
|
The variable to bound. |
required | |
partially_corrected_preds_for_x
|
Predictions tensor of shape |
required | |
left_bounds
|
Unused in this routine (kept for signature symmetry). |
required | |
right_bounds
|
Candidate right bounds tensor of shape |
required | |
constraints_at_level_x
|
Constraints used to test feasibility of each bound. |
required |
Returns:
| Type | Description |
|---|---|
|
A tensor of shape |
|
|
or the |
Source code in pishield/qflra_requirements/correct_predictions.py
get_closest_left_bound_wv ¶
get_closest_left_bound_wv(x, partially_corrected_preds_for_x, left_bounds, right_bounds, constraints_at_level_x)
Pick the smallest feasible left bound greater than the closest right bound.
Alternative bound-selection variant that first finds the greatest feasible right
bound not exceeding x, then returns the smallest left bound strictly above
it. Note: this variant does not treat vacuous constraints.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x
|
The variable to bound. |
required | |
partially_corrected_preds_for_x
|
Predictions tensor of shape |
required | |
left_bounds
|
Candidate left bounds tensor of shape |
required | |
right_bounds
|
Candidate right bounds tensor of shape |
required | |
constraints_at_level_x
|
Constraints used to test feasibility of each bound. |
required |
Returns:
| Type | Description |
|---|---|
|
A tensor of shape |
|
|
selected left bound per sample. |
Source code in pishield/qflra_requirements/correct_predictions.py
get_closest_right_bound_wv ¶
get_closest_right_bound_wv(x, partially_corrected_preds_for_x, left_bounds, right_bounds, constraints_at_level_x)
Pick the greatest feasible right bound less than the closest left bound.
Alternative bound-selection variant that first finds the smallest feasible left
bound strictly above x, then returns the greatest right bound below it. Note:
this variant does not treat vacuous constraints.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x
|
The variable to bound. |
required | |
partially_corrected_preds_for_x
|
Predictions tensor of shape |
required | |
left_bounds
|
Candidate left bounds tensor of shape |
required | |
right_bounds
|
Candidate right bounds tensor of shape |
required | |
constraints_at_level_x
|
Constraints used to test feasibility of each bound. |
required |
Returns:
| Type | Description |
|---|---|
|
A tensor of shape |
|
|
selected right bound per sample. |
Source code in pishield/qflra_requirements/correct_predictions.py
get_closest_left_and_right_bounds ¶
get_closest_left_and_right_bounds(x: Variable, partially_corrected_preds_for_x: Tensor, constraints_at_level_x: List[Constraint]) -> Tuple[torch.Tensor, torch.Tensor]
Compute the closest feasible left and right bounds for a variable.
Splits the constraints by the sign of x, computes the candidate left and
right bounds, then selects the feasible bound closest to x on each side.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x
|
Variable
|
The variable to bound. |
required |
partially_corrected_preds_for_x
|
Tensor
|
Predictions tensor of shape |
required |
constraints_at_level_x
|
List[Constraint]
|
Constraints bounding |
required |
Returns:
| Type | Description |
|---|---|
Tuple[Tensor, Tensor]
|
A tuple |
Source code in pishield/qflra_requirements/correct_predictions.py
get_closest_bound ¶
get_closest_bound(x_vals_violating_req: Tensor, left_bounds: Tensor, right_bounds: Tensor) -> torch.Tensor
Snap violating values to whichever of two bounds is nearer.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x_vals_violating_req
|
Tensor
|
Tensor of current |
required |
left_bounds
|
Tensor
|
Per-sample left bound candidates. |
required |
right_bounds
|
Tensor
|
Per-sample right bound candidates. |
required |
Returns:
| Type | Description |
|---|---|
Tensor
|
A tensor with each value replaced by the closer of its left or right bound. |
Source code in pishield/qflra_requirements/correct_predictions.py
compute_DRL ¶
compute_DRL(x: Variable, partially_corrected_preds: Tensor, constraints_at_level_x: List[Constraint]) -> torch.Tensor
Correct a variable against disjunctive constraints.
Identifies samples violating the (disjunctive) constraints at x's level and
moves only those values to the nearest feasible bound, leaving satisfying samples
unchanged.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x
|
Variable
|
The variable being corrected. |
required |
partially_corrected_preds
|
Tensor
|
Predictions tensor of shape |
required |
constraints_at_level_x
|
List[Constraint]
|
Constraints bounding |
required |
Returns:
| Type | Description |
|---|---|
Tensor
|
A tensor of shape |
Source code in pishield/qflra_requirements/correct_predictions.py
correct_preds ¶
correct_preds(preds: Tensor, ordering: List[Variable], sets_of_constr: {Variable: List[Constraint]})
Correct predictions so they satisfy all QFLRA requirements.
Processes the variables in the given ordering, correcting each variable into
the feasible region implied by its constraint set given the already-corrected
values of the earlier variables. Purely conjunctive levels are handled by simple
interval clipping; levels with disjunctions are handled by :func:compute_DRL.
Infinite corrected values are replaced by a large finite sentinel.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
preds
|
Tensor
|
Predictions tensor of shape |
required |
ordering
|
List[Variable]
|
The variable ordering driving the correction sweep. |
required |
sets_of_constr
|
{Variable: List[Constraint]}
|
Mapping from each :class: |
required |
Returns:
| Type | Description |
|---|---|
|
A tensor of shape |
|
|
constraints. |
Example
sets_of_constr = compute_sets_of_constraints(ordering, constraints, verbose=False) corrected = correct_preds(preds, ordering, sets_of_constr)
Source code in pishield/qflra_requirements/correct_predictions.py
check_all_constraints_sat ¶
Check that corrected predictions satisfy every constraint.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
corrected_preds
|
The corrected predictions tensor. |
required | |
constraints
|
The constraints to verify. |
required | |
error_raise
|
If |
True
|
Returns:
| Type | Description |
|---|---|
|
|
|
|
|
Raises:
| Type | Description |
|---|---|
Exception
|
If |
Source code in pishield/qflra_requirements/correct_predictions.py
example_predictions_custom ¶
Build a small example batch of predictions for demonstration.
Returns:
| Type | Description |
|---|---|
|
A tensor of shape |
Source code in pishield/qflra_requirements/correct_predictions.py
main ¶
Run an end-to-end demo: parse constraints, correct example preds, report.
Source code in pishield/qflra_requirements/correct_predictions.py
Feature orderings¶
feature_orderings ¶
Selection of the variable ordering used to correct predictions.
The ordering determines the sequence in which variables are corrected by the Shield Layer; it can be taken as given or randomised.
set_random_ordering ¶
Shuffle a variable ordering in place.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
ordering
|
List[Variable]
|
List of :class: |
required |
Returns:
| Type | Description |
|---|---|
|
The same list, shuffled in place. |
Source code in pishield/qflra_requirements/feature_orderings.py
set_ordering ¶
Select a variable ordering according to a choice string.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
ordering
|
List[Variable]
|
The base list of :class: |
required |
label_ordering_choice
|
str
|
Either |
required |
Returns:
| Type | Description |
|---|---|
|
The chosen ordering as a list of :class: |
Source code in pishield/qflra_requirements/feature_orderings.py
Atom utilities¶
utils_atoms ¶
Helper functions for manipulating lists of linear inequality atoms.
These utilities merge, scale and negate the :class:Atom objects that make up the
body (left-hand side) of QFLRA inequalities.
collapse_atoms ¶
Merge duplicated atoms that refer to the same variable.
Atoms that share a variable are combined by summing their signed coefficients. Any atom whose coefficients cancel out (resulting coefficient of zero) is dropped.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
atom_list
|
List of :class: |
required |
Returns:
| Type | Description |
|---|---|
|
A list of :class: |
Source code in pishield/qflra_requirements/utils_atoms.py
multiply_coefficients_of_atoms ¶
Scale every atom's coefficient by a constant factor.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
atoms
|
List[Atom]
|
List of :class: |
required |
coeff
|
float
|
Multiplicative factor applied to each atom's coefficient. |
required |
Returns:
| Type | Description |
|---|---|
|
A new list of :class: |
|
|
preserved). |
Source code in pishield/qflra_requirements/utils_atoms.py
negate_atoms ¶
Flip the sign of every atom in a list.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
atoms
|
List[Atom]
|
List of :class: |
required |
Returns:
| Type | Description |
|---|---|
|
A new list of :class: |
Source code in pishield/qflra_requirements/utils_atoms.py
Function utilities¶
utils_functions ¶
Evaluation and statistics helpers for QFLRA constraints.
Functions here evaluate inequality atoms on batched predictions, detect missing values and disjunctions, and report constraint-satisfaction statistics.
eval_atoms_list ¶
Evaluate a list of atoms on batched predictions and reduce them.
Each atom is evaluated at the predicted value of its variable, then the
per-atom results are combined according to reduction.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
atoms_list
|
List[Atom]
|
The :class: |
required |
preds
|
Tensor
|
Predictions tensor of shape |
required |
reduction
|
How to combine atoms across the body. Only |
'sum'
|
Returns:
| Type | Description |
|---|---|
|
A tensor of shape |
|
|
all-zero tensor if |
Raises:
| Type | Description |
|---|---|
Exception
|
If |
Source code in pishield/qflra_requirements/utils_functions.py
any_disjunctions_in_constraint_set ¶
Report whether any constraint is a disjunction of inequalities.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
constraints
|
List[Constraint]
|
The constraints to inspect. |
required |
Returns:
| Type | Description |
|---|---|
|
|
|
|
'or'), |
Source code in pishield/qflra_requirements/utils_functions.py
get_missing_mask ¶
Identify samples whose involved variable values indicate missing data.
A sample is flagged as missing when the product of the raw values of the
relevant variables falls below -TOLERANCE (the convention used to encode
missing values in this codebase).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
ineq_atoms
|
List[Atom]
|
Atoms (or list of atom lists) whose variables are checked. |
required |
preds
|
Tensor
|
Predictions tensor of shape |
required |
Returns:
| Type | Description |
|---|---|
|
A boolean tensor of shape |
|
|
values. |
Source code in pishield/qflra_requirements/utils_functions.py
split_constr_atoms ¶
Separate the atom of a variable from the rest of a constraint body.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
y
|
Variable
|
The variable to extract. |
required |
constr
|
Constraint
|
The constraint whose body atoms are split. |
required |
Returns:
| Type | Description |
|---|---|
|
A tuple |
|
|
is the (positive) coefficient of |
|
|
remaining body atoms. |
Source code in pishield/qflra_requirements/utils_functions.py
get_samples_violating_constraints ¶
Flag samples that violate at least one constraint.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
constraints
|
The constraints to check. |
required | |
preds
|
Predictions tensor of shape |
required |
Returns:
| Type | Description |
|---|---|
|
A boolean tensor of shape |
|
|
constraint. |
Source code in pishield/qflra_requirements/utils_functions.py
check_all_constraints_are_sat ¶
Print which constraints are violated before and after correction.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
constraints
|
The constraints to check. |
required | |
preds
|
The original predictions tensor. |
required | |
corrected_preds
|
The predictions after correction. |
required |
Returns:
| Type | Description |
|---|---|
|
|
|
|
otherwise. |
Source code in pishield/qflra_requirements/utils_functions.py
compute_sat_stats ¶
Compute per-constraint and overall constraint-satisfaction statistics.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
real_data
|
Tensor of data/predictions of shape |
required | |
constraints
|
The constraints to evaluate. |
required | |
mask_out_missing_values
|
If |
False
|
Returns:
| Type | Description |
|---|---|
|
A tuple |
|
|
of pandas DataFrames: per-constraint satisfaction rates (as percentages) and |
|
|
the overall percentage of samples violating constraints. |