Skip to content

FIX/refactor: centralize CP MiniZinc model assembly#445

Merged
peacker merged 1 commit intodevelopfrom
refactor/cp-minizinc-model-assembler
Apr 28, 2026
Merged

FIX/refactor: centralize CP MiniZinc model assembly#445
peacker merged 1 commit intodevelopfrom
refactor/cp-minizinc-model-assembler

Conversation

@juaninf
Copy link
Copy Markdown
Collaborator

@juaninf juaninf commented Apr 28, 2026

CP MiniZinc Model Parts Standardization

CLAASP has many CP model generators. These generators turn MiniZinc fragments into full MiniZinc models. The fragments come from several sources:

  1. Component MiniZinc generators, which emit MiniZinc declarations and constraints.
  2. MiniZinc includes, for example:
include "globals.mzn";
  1. Custom MiniZinc predicates/functions used by component constraints.
  2. MiniZinc output directives, for example:
output ["plaintext = " ++ show(plaintext) ++ "\n"];

Currently there are several structural problems.

1. Fragment Names Are Not Consistent

Different models use different attributes for the same conceptual fragment.

Example: in claasp/cipher_modules/models/cp/mzn_models/mzn_xor_differential_trail_search_fixing_number_of_active_sboxes_model.py, the first-step model stores generated constraints in:

constraints = self.fix_variables_value_constraints(fixed_variables, "first_step")
self._first_step = constraints

But the second-step model stores generated constraints in:

self._model_constraints.extend(self.final_xor_differential_constraints(weight))

So the same kind of fragment, MiniZinc constraints, is stored under two different names depending on the sub-model.

A clearer design would use explicit model parts:

self.first_step_parts.constraints
self.second_step_parts.constraints

2. _model_prefix Has Multiple Meanings

Many models use:

self._model_prefix

but it does not always mean only "prefix".

In the base model, it starts as MiniZinc includes and helper predicates/functions:

self._model_prefix = [
    'include "globals.mzn";',
    usefulfunctions.MINIZINC_USEFUL_FUNCTIONS,
]

But some models also add declarations to it.

Example from mzn_xor_differential_model.py:

variables, constraints = self.input_xor_differential_constraints()
self._model_prefix.extend(variables)

Here variables contains declarations such as:

array[0..31] of var 0..1: plaintext;
array[0..63] of var 0..1: key;

So _model_prefix becomes:

includes + helper predicates/functions + declarations

A cleaner contract would be:

prefix       = includes + helper predicates/functions/constants
variables    = declarations
constraints  = constraints + solve directive
outputs      = output directives

3. Some Attributes Change Meaning During Build

Some attributes start as one kind of fragment and later become a full MiniZinc model.

Example from the first-step model:

self._first_step = constraints

At this point:

self._first_step

means:

first-step constraints

Later:

self._first_step = self._model_prefix + self._variables_list + self._first_step

Now:

self._first_step

means:

full first-step MiniZinc model

The same happens with _model_constraints.

Example from mzn_cipher_model.py:

self._model_constraints = constraints

At this point it means constraints only.

Later:

self._model_constraints = self._model_prefix + self._model_constraints

Now it means:

full MiniZinc model prefix + constraints

4. Model Assembly Order Is Not Uniform

Different models assemble MiniZinc text in different ways.

First-step model example from mzn_xor_differential_number_of_active_sboxes_model.py:

self._first_step = self._model_prefix + self._variables_list + self._first_step

Order:

prefix + variables + constraints

Other models copy only the prefix into _model_constraints.

Example from mzn_cipher_model.py:

self._model_constraints = self._model_prefix + self._model_constraints

Example from mzn_xor_differential_model.py:

self._model_constraints = self._model_prefix + self._model_constraints

Example from mzn_xor_linear_model.py:

self._model_constraints = self._model_prefix + self._model_constraints

Order:

prefix + constraints

Some solve paths then assemble with:

self._variables_list + self._model_constraints

For example, in the base solve() path:

mzn_model = self._variables_list + self._model_constraints

This works only because many builders already copied the prefix into _model_constraints.

That makes the final MiniZinc model dependent on hidden assumptions about whether _model_constraints is raw constraints or already a full model.

A cleaner design would use one assembly method:

def assemble_model(parts):
    return (
        parts.prefix
        + parts.variables
        + parts.constraints
        + parts.outputs
    )

5. Two-Step Models Are Multiple Self-Contained Models

The two-step flow is not one model. It has at least:

first-step MiniZinc model
second-step MiniZinc model

Currently the first one is stored in:

self._first_step

and the second one is stored using the normal model fields:

self._variables_list
self._model_constraints

But both are self-contained MiniZinc models.

A clearer representation would be:

self.first_step_parts = MiniZincModelParts(...)
self.second_step_parts = MiniZincModelParts(...)

Then both models follow the same structure:

prefix
variables
constraints
outputs

The intention of this PR is only to introduce MiniZincModelParts as the new way to manage MiniZinc model parts. This class provides the assemble_model flow, which for now preserves the existing behavior even when the current fragments still mix variable declarations with constraints, or declarations with includes.

In other words, this PR does not fully reorganize every CP model yet. Its goal is to introduce the structure and give reviewers a clear view of the new direction for managing MiniZinc fragments. In the next PR, we will inspect the models one by one and move each fragment into the correct part.

Note:
The issues described above were revealed and confirmed while working on PR #444: #444. That PR aims to ensure CP models do not include MiniZinc predicates/functions that they do not actually use.

Co-authored-by: Copilot <copilot@github.com>
@sonarqubecloud
Copy link
Copy Markdown

@juaninf juaninf marked this pull request as ready for review April 28, 2026 07:57
@peacker peacker merged commit 6d6061a into develop Apr 28, 2026
14 of 20 checks passed
@peacker peacker deleted the refactor/cp-minizinc-model-assembler branch April 28, 2026 09:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants