Skip to content

core

Introduction of core module

jijmodeling_transpiler.core provides core method for jijmodeling_transpiler. This module corresponds Engine layer of JijZept layer diagram. The layer is constructed three parts:

  1. compile
  2. transpile (encode)
  3. decode, evaluate

1. Compile

In compile process, an instance data is substituted to Placeholders of a user defined model. compile_model function provides that projection in JijModelingTranspiler.

Example

import jijmodeling as jm
import jijmodeling_transpiler as jmt
n = jm.Placeholder("n")
x = jm.Binary("x", (n, n))
i = jm.Element("i", n)
problem = jm.Problem("sample")
problem += x[:, :]
problem += jm.Constraint("onehot", x[:, i], forall=i)
compiled_instance = jmt.core.compile_model(problem, {"n": 2}, {})
compiled_instance

The user can directly manipulate this CompiledInstance. Please refer to the CompiledInstance class reference, SubstitutedExpression class reference, and the VariableMap class for the correspondence between each variable and the integer index.

2. Transpile (encode)

This process have to seperate from core module because the process depends each solver. However currently the process include in core module.

3. Decode, Evaluate

For which solver did the decoding process transpile in the transpile process? how did you encode it? depends on. Therefore, the builder object output by the Transpile process is also required for the decoding process.

CompiledInstance dataclass

CompiledInstance: object return compile_model method.

Attributes:

Name Type Description
sense ProblemSense

problem sense minimize or maximize.

objective SubstitutedExpression

objective expression.

constraint dict[str, dict[tuple[int, ...], SubstitutedExpression]]

constraints. str key represents name of constraint. tuple[int,...] is values of forall index.

penalty dict[str, dict[tuple[int, ...], SubstitutedExpression]]

dict[str, dict[tuple[int, ...], SubstitutedExpression]]

var_map VariableMap

VariableMap

data InstanceData

InstanceData

problem Problem

jijmodeling.InstanceData

Examples:

import jijmodeling as jm
import jijmodeling_transpiler as jmt
n = jm.Placeholder("n")
x = jm.Binary("x", (n, n))
i = jm.Element("i", n)
problem = jm.Problem("sample")
problem += x[:, :]
problem += jm.Constraint("onehot", x[:, i], forall=i)
compiled_instance = jmt.core.compile_model(problem, {"n": 2}, {})
compiled_instance
CompiledInstance(
    objective=SubstitutedExpression(
        linear=LinearSubstitutedExpr(coeff={0: 1.0, 1: 1.0, 2: 1.0, 3: 1.0}, constant=0.0),
        nonlinear=None),
    constraint={
        'onehot': {
            (0,): SubstitutedExpression(
                    linear=LinearSubstitutedExpr(coeff={0: 1.0, 1: 1.0}, constant=0.0),
                    nonlinear=None),
            (1,): SubstitutedExpression(
                    linear=LinearSubstitutedExpr(coeff={2: 1.0, 3: 1.0}, constant=0.0),
                    nonlinear=None)}
            },
    penalty={},
    var_map=VariableMap(var_map={'x': {(0, 0): 0, (0, 1): 2, (1, 0): 1, (1, 1): 3}},
                        var_num=4,
                        integer_bound={}),
    ...
)
Source code in jijmodeling_transpiler/core/compile/compiled_model.py
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
@dataclass
class CompiledInstance:
    """CompiledInstance: object return `compile_model` method.

    Attributes:
        sense (jijmodeling.ProblemSense): problem sense minimize or maximize.
        objective (SubstitutedExpression): objective expression.
        constraint (dict[str, dict[tuple[int, ...], SubstitutedExpression]]): constraints. str key represents name of constraint. tuple[int,...] is values of forall index.
        penalty: dict[str, dict[tuple[int, ...], SubstitutedExpression]]
        var_map: VariableMap
        data: InstanceData
        problem: jijmodeling.InstanceData

    Examples:
        ```python
        import jijmodeling as jm
        import jijmodeling_transpiler as jmt
        n = jm.Placeholder("n")
        x = jm.Binary("x", (n, n))
        i = jm.Element("i", n)
        problem = jm.Problem("sample")
        problem += x[:, :]
        problem += jm.Constraint("onehot", x[:, i], forall=i)
        compiled_instance = jmt.core.compile_model(problem, {"n": 2}, {})
        compiled_instance
        ```

        ```
        CompiledInstance(
            objective=SubstitutedExpression(
                linear=LinearSubstitutedExpr(coeff={0: 1.0, 1: 1.0, 2: 1.0, 3: 1.0}, constant=0.0),
                nonlinear=None),
            constraint={
                'onehot': {
                    (0,): SubstitutedExpression(
                            linear=LinearSubstitutedExpr(coeff={0: 1.0, 1: 1.0}, constant=0.0),
                            nonlinear=None),
                    (1,): SubstitutedExpression(
                            linear=LinearSubstitutedExpr(coeff={2: 1.0, 3: 1.0}, constant=0.0),
                            nonlinear=None)}
                    },
            penalty={},
            var_map=VariableMap(var_map={'x': {(0, 0): 0, (0, 1): 2, (1, 0): 1, (1, 1): 3}},
                                var_num=4,
                                integer_bound={}),
            ...
        )
        ```
    """

    sense: ProblemSense
    objective: SubstitutedExpression
    constraint: dict[str, dict[tuple[int, ...], SubstitutedExpression]]
    penalty: dict[str, dict[tuple[int, ...], SubstitutedExpression]]
    var_map: VariableMap
    data: InstanceData
    problem: Problem
    deci_var_shape: dict[str, tuple[int | None, ...]]

    def add(self, source: CompiledInstance):
        self.objective.add(source.objective)

        for label, constraint in source.constraint.items():
            if label not in self.constraint:
                self.constraint[label] = constraint
                self.problem.constraints[label] = source.problem.constraints[label]
            else:
                for forall, const_expr in constraint.items():
                    if forall in self.constraint[label]:
                        self.constraint[label][forall].add(const_expr)
                    else:
                        self.constraint[label][forall] = const_expr

        for label, penalty in source.penalty.items():
            if label not in self.penalty:
                self.penalty[label] = penalty
            else:
                for forall, pena_expr in penalty.items():
                    if forall in self.penalty[label]:
                        self.penalty[label][forall].add(pena_expr)
                    else:
                        self.penalty[label][forall] = pena_expr

InstanceData

Instance Data class The constructor provides type validation and conversion from user's input to valid instance data type.

Attributes:

Name Type Description
tensor_data dict[str, ndarray]

tensor value of Placheolder. The key is uuid of the corresponding Placheolder.

jagged_data dict[str, list]

jagged array value of JaggedArray. The key is uuid of the corresponding JaggedArray.

fixed_variables dict[str, dict[tuple[int, ...], float]]

fixed variable values.

indices dict[str, int]

value of Element. The attribute is changing in compile process.

Source code in jijmodeling_transpiler/core/instance_data.py
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
class InstanceData:
    """Instance Data class
    The constructor provides type validation and conversion from user's input to valid instance data type.

    Attributes:
        tensor_data (dict[str, numpy.ndarray]): tensor value of Placheolder. The key is uuid of the corresponding Placheolder.
        jagged_data (dict[str, list]): jagged array value of JaggedArray. The key is uuid of the corresponding JaggedArray.
        fixed_variables (dict[str, dict[tuple[int, ...], float]]): fixed variable values.
        indices (dict[str, int]): value of Element. The attribute is changing in compile process.
    """

    def __init__(
        self,
        instance_data: dict,
        fixed_vars: dict,
        ph_names: list[str],
        indices: typ.Optional[dict] = None,
    ) -> None:
        """

        Args:
            instance_data (dict): user input instance_data.
            fixed_vars (dict): user input fixed_variables.
            ph_vars (list[name]): Placeholder list include user defined model. This is used for validation.
            indices (typ.Optional[dict], optional): value of each Elements. Defaults to None.

        Raises:
            ValueError: user's input is invalid.
        """
        name_key_data = {}

        for ph_v in ph_names:
            if ph_v in instance_data:
                name_key_data[ph_v] = instance_data[ph_v]
            else:
                raise ValueError(f"Placeholder `{ph_v}` is needed in instance_data.")

        # Store data, even if the data is not include of the problem,
        for ph_name, ph_value in instance_data.items():
            if isinstance(ph_name, str):
                if ph_name not in name_key_data:
                    name_key_data[ph_name] = ph_value

        _ph_value = {}
        _jagged_value = {}
        for ph_v in ph_names:
            try:
                _ph_value[ph_v] = np.array(name_key_data[ph_v])
            except ValueError:
                _jagged_value[ph_v] = name_key_data[ph_v]

        for ph_name, ph_value in name_key_data.items():
            if ph_name not in _ph_value:
                try:
                    _ph_value[ph_name] = np.array(ph_value)
                except ValueError:
                    _jagged_value[ph_name] = ph_value

        raise_type_error(
            "fixed_vars",
            fixed_vars,
            dict[str, dict[tuple[int, ...], typ.Union[float, int]]],
        )

        self.tensor_data: dict[str, np.ndarray] = _ph_value
        self.jagged_data: dict[str, list] = _jagged_value
        self.fixed_variables: dict[str, dict[tuple[int, ...], float]] = fixed_vars
        self.indices: dict[str, int] = indices if indices is not None else {}

    def get_value(self, uuid: str, subscripts: tuple[int, ...]) -> float:
        try:
            if uuid in self.tensor_data:
                return self.tensor_data[uuid][subscripts]
            else:
                ph_value = self.jagged_data[uuid]
                for s in subscripts:
                    ph_value = ph_value[s]
                return ph_value
        except IndexError as e:
            raise SubstituteError(f"IndexError: {e}") from e
        except KeyError as e:
            raise SubstituteError(f"KeyError: {e}") from e

    def get_length(self, name: str, subscripts: tuple[int, ...], axis: int) -> int:
        try:
            if name in self.tensor_data:
                return self.tensor_data[name][subscripts].shape[axis]
            else:
                if axis != 0:
                    raise SubstituteError(
                        f"JaggedArray can not get length of axis {axis}"
                    )
                ph_value = self.jagged_data[name]
                for s in subscripts:
                    ph_value = ph_value[s]
                return len(ph_value)
        except IndexError as e:
            raise SubstituteError(f"IndexError: {e}") from e
        except KeyError as e:
            raise SubstituteError(f"KeyError: {e}") from e

__init__(instance_data, fixed_vars, ph_names, indices=None)

Parameters:

Name Type Description Default
instance_data dict

user input instance_data.

required
fixed_vars dict

user input fixed_variables.

required
ph_vars list[name]

Placeholder list include user defined model. This is used for validation.

required
indices Optional[dict]

value of each Elements. Defaults to None.

None

Raises:

Type Description
ValueError

user's input is invalid.

Source code in jijmodeling_transpiler/core/instance_data.py
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
def __init__(
    self,
    instance_data: dict,
    fixed_vars: dict,
    ph_names: list[str],
    indices: typ.Optional[dict] = None,
) -> None:
    """

    Args:
        instance_data (dict): user input instance_data.
        fixed_vars (dict): user input fixed_variables.
        ph_vars (list[name]): Placeholder list include user defined model. This is used for validation.
        indices (typ.Optional[dict], optional): value of each Elements. Defaults to None.

    Raises:
        ValueError: user's input is invalid.
    """
    name_key_data = {}

    for ph_v in ph_names:
        if ph_v in instance_data:
            name_key_data[ph_v] = instance_data[ph_v]
        else:
            raise ValueError(f"Placeholder `{ph_v}` is needed in instance_data.")

    # Store data, even if the data is not include of the problem,
    for ph_name, ph_value in instance_data.items():
        if isinstance(ph_name, str):
            if ph_name not in name_key_data:
                name_key_data[ph_name] = ph_value

    _ph_value = {}
    _jagged_value = {}
    for ph_v in ph_names:
        try:
            _ph_value[ph_v] = np.array(name_key_data[ph_v])
        except ValueError:
            _jagged_value[ph_v] = name_key_data[ph_v]

    for ph_name, ph_value in name_key_data.items():
        if ph_name not in _ph_value:
            try:
                _ph_value[ph_name] = np.array(ph_value)
            except ValueError:
                _jagged_value[ph_name] = ph_value

    raise_type_error(
        "fixed_vars",
        fixed_vars,
        dict[str, dict[tuple[int, ...], typ.Union[float, int]]],
    )

    self.tensor_data: dict[str, np.ndarray] = _ph_value
    self.jagged_data: dict[str, list] = _jagged_value
    self.fixed_variables: dict[str, dict[tuple[int, ...], float]] = fixed_vars
    self.indices: dict[str, int] = indices if indices is not None else {}

IntegerBound dataclass

IntegerBound

Attributes:

Name Type Description
lower int | float

lower bound of integer decision variable

upper int | float

upper bound of integer decision variable

Source code in jijmodeling_transpiler/core/variable_map.py
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
@dataclass
class IntegerBound:
    """IntegerBound

    Attributes:
        lower (int | float): lower bound of integer decision variable
        upper (int | float): upper bound of integer decision variable

    """

    lower: int | float
    upper: int | float

VariableMap dataclass

VarialeMap

This class is used to manage the mapping between the label and the index of the decision variable.

Attributes:

Name Type Description
var_map dict[str, dict[tuple[int, ...], int]]

label, subscripts -> index (int)

var_num int

number of variables

integer_bound dict[str, dict[tuple[int, ...], IntegerBound]]

bounds of integer decision variables.

Source code in jijmodeling_transpiler/core/variable_map.py
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
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
@dataclass
class VariableMap:
    """VarialeMap

    This class is used to manage the mapping between the label and the index of the decision variable.

    Attributes:
        var_map (dict[str, dict[tuple[int, ...], int]]): label, subscripts -> index (int)
        var_num (int): number of variables
        integer_bound (dict[str, dict[tuple[int, ...], IntegerBound]]): bounds of integer decision variables.

    """

    var_map: dict[str, dict[tuple[int, ...], int]]
    var_num: int
    integer_bound: dict[str, dict[tuple[int, ...], IntegerBound]]

    def add_variable(self, label: str, subscripts: tuple[int, ...]) -> int:
        """Add a variable to the variable map.

        Args:
            label (str): label of the variable
            subscripts (tuple[int, ...]): subscripts of the variable

        Returns:
            int: index of the variable in the variable map
        """
        if label not in self.var_map:
            self.var_map[label] = {}

        if subscripts not in self.var_map[label]:
            self.var_map[label][subscripts] = self.var_num
            self.var_num += 1
            return self.var_num - 1
        else:
            return self.var_map[label][subscripts]

    def add_int_variable(self, label, subscripts, lower, upper) -> int:
        """Add an integer variable to the variable map."""
        if label not in self.integer_bound:
            self.integer_bound[label] = {}
        self.integer_bound[label][subscripts] = IntegerBound(lower=lower, upper=upper)
        var_index = self.add_variable(label, subscripts)
        return var_index

    def to_serializable(self) -> dict:
        """Convert to a serializable object.

        Returns:
            dict: serializable object

        Examples:
            ```python
            varmap = VariableMap()
            varmap.add_variable("x", (0, 0))
            varmap.add_variable("x", (0, 1))
            varmap.to_serializable()
            # {
            #    "class": "VariableMap",
            #    "var_map": {
            #        "x": {
            #            "subscripts": [
            #                [0, 0],
            #                [0, 1]
            #            ],
            #            "index": [0, 1]
            #        }
            #    },
            #    "var_num": 2,
            #    "integer_bound": {}
            # }
            ```
        """
        varmap_serializable = {}
        for label, subscripts_map in self.var_map.items():
            varmap_serializable[label] = {"subscripts": [], "index": []}
            for subscripts, index in subscripts_map.items():
                varmap_serializable[label]["subscripts"].append(subscripts)
                varmap_serializable[label]["index"].append(index)
        integer_bound_serializable = {}
        for label, subscripts_map in self.integer_bound.items():
            integer_bound_serializable[label] = {"subscripts": [], "bound": []}
            for subscripts, bound in subscripts_map.items():
                integer_bound_serializable[label]["subscripts"].append(subscripts)
                integer_bound_serializable[label]["bound"].append(
                    {"lower": bound.lower, "upper": bound.upper}
                )
        return {
            "class": "VariableMap",
            "var_map": varmap_serializable,
            "var_num": self.var_num,
            "integer_bound": integer_bound_serializable,
        }

    @classmethod
    def from_serializable(cls, data: dict) -> "VariableMap":
        if data["class"] != "VariableMap":
            raise ValueError(f"Invalid class: {data['class']}")
        var_map = {}
        for label, subscripts_map in data["var_map"].items():
            var_map[label] = {
                tuple(subscripts): index
                for subscripts, index in zip(
                    subscripts_map["subscripts"], subscripts_map["index"]
                )
            }
        integer_bound = {}
        for label, subscripts_map in data["integer_bound"].items():
            integer_bound[label] = {
                tuple(subscripts): IntegerBound(bound["lower"], bound["upper"])
                for subscripts, bound in zip(
                    subscripts_map["subscripts"], subscripts_map["bound"]
                )
            }
        return VariableMap(var_map, data["var_num"], integer_bound)

add_int_variable(label, subscripts, lower, upper)

Add an integer variable to the variable map.

Source code in jijmodeling_transpiler/core/variable_map.py
56
57
58
59
60
61
62
def add_int_variable(self, label, subscripts, lower, upper) -> int:
    """Add an integer variable to the variable map."""
    if label not in self.integer_bound:
        self.integer_bound[label] = {}
    self.integer_bound[label][subscripts] = IntegerBound(lower=lower, upper=upper)
    var_index = self.add_variable(label, subscripts)
    return var_index

add_variable(label, subscripts)

Add a variable to the variable map.

Parameters:

Name Type Description Default
label str

label of the variable

required
subscripts tuple[int, ...]

subscripts of the variable

required

Returns:

Name Type Description
int int

index of the variable in the variable map

Source code in jijmodeling_transpiler/core/variable_map.py
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
def add_variable(self, label: str, subscripts: tuple[int, ...]) -> int:
    """Add a variable to the variable map.

    Args:
        label (str): label of the variable
        subscripts (tuple[int, ...]): subscripts of the variable

    Returns:
        int: index of the variable in the variable map
    """
    if label not in self.var_map:
        self.var_map[label] = {}

    if subscripts not in self.var_map[label]:
        self.var_map[label][subscripts] = self.var_num
        self.var_num += 1
        return self.var_num - 1
    else:
        return self.var_map[label][subscripts]

to_serializable()

Convert to a serializable object.

Returns:

Name Type Description
dict dict

serializable object

Examples:

varmap = VariableMap()
varmap.add_variable("x", (0, 0))
varmap.add_variable("x", (0, 1))
varmap.to_serializable()
# {
#    "class": "VariableMap",
#    "var_map": {
#        "x": {
#            "subscripts": [
#                [0, 0],
#                [0, 1]
#            ],
#            "index": [0, 1]
#        }
#    },
#    "var_num": 2,
#    "integer_bound": {}
# }
Source code in jijmodeling_transpiler/core/variable_map.py
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
def to_serializable(self) -> dict:
    """Convert to a serializable object.

    Returns:
        dict: serializable object

    Examples:
        ```python
        varmap = VariableMap()
        varmap.add_variable("x", (0, 0))
        varmap.add_variable("x", (0, 1))
        varmap.to_serializable()
        # {
        #    "class": "VariableMap",
        #    "var_map": {
        #        "x": {
        #            "subscripts": [
        #                [0, 0],
        #                [0, 1]
        #            ],
        #            "index": [0, 1]
        #        }
        #    },
        #    "var_num": 2,
        #    "integer_bound": {}
        # }
        ```
    """
    varmap_serializable = {}
    for label, subscripts_map in self.var_map.items():
        varmap_serializable[label] = {"subscripts": [], "index": []}
        for subscripts, index in subscripts_map.items():
            varmap_serializable[label]["subscripts"].append(subscripts)
            varmap_serializable[label]["index"].append(index)
    integer_bound_serializable = {}
    for label, subscripts_map in self.integer_bound.items():
        integer_bound_serializable[label] = {"subscripts": [], "bound": []}
        for subscripts, bound in subscripts_map.items():
            integer_bound_serializable[label]["subscripts"].append(subscripts)
            integer_bound_serializable[label]["bound"].append(
                {"lower": bound.lower, "upper": bound.upper}
            )
    return {
        "class": "VariableMap",
        "var_map": varmap_serializable,
        "var_num": self.var_num,
        "integer_bound": integer_bound_serializable,
    }

compile_model(problem, instance, fixed_variables=None)

Compile a problem and an instance into a compiled model.

Parameters:

Name Type Description Default
problem Problem

a problem to be compiled

required
instance dict[str, ndarray | list | int | float]

an instance to be compiled

required
fixed_variables dict[dict, dict[tuple[int, ...], float]]

fixed variables. Defaults to None.

None

Returns:

Name Type Description
CompiledInstance CompiledInstance

a compiled model

Source code in jijmodeling_transpiler/core/compile/compile.py
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
def compile_model(
    problem: jm.Problem,
    instance: dict[str, np.ndarray | list | int | float],
    fixed_variables: typ.Optional[dict[dict, dict[tuple[int, ...], float]]] = None,
) -> CompiledInstance:
    """Compile a problem and an instance into a compiled model.

    Args:
        problem (jm.Problem): a problem to be compiled
        instance (dict[str, np.ndarray | list | int | float]): an instance to be compiled
        fixed_variables (dict[dict, dict[tuple[int, ...], float]], optional): fixed variables. Defaults to None.

    Returns:
        CompiledInstance: a compiled model
    """
    if fixed_variables is None:
        fixed_variables = {}

    # Convert the problem to a substitutable problem
    substitutable_problem = convert_problem_to_substutitable(problem)

    # Validate and create an instance of InstanceData
    ph_vars = _get_ph_vars(substitutable_problem)
    ph_names = [p.name for p in ph_vars]
    instance_data = InstanceData(instance, fixed_variables, ph_names, indices={})

    var_map = VariableMap({}, var_num=0, integer_bound={})

    fixed_vars, non_trivial_constraint = fixed_vars_from_trivial_constraint(
        substitutable_problem, instance_data
    )
    instance_data.fixed_variables = fixed_vars

    # Objective function
    try:
        objective = substitutable_problem.objective.substitute(var_map, instance_data)
    except SubstituteError as e:
        raise SubstituteError(f"In objective: {e}")

    # Constraint
    constraint_expr: dict[str, dict[tuple[int, ...], SubstitutedExpression]] = {}
    for const_label, constraint in non_trivial_constraint.items():
        constraint_expr[const_label] = constraint_substitute(
            constraint, instance_data, var_map
        )

    penalty_expr = {}
    for pena_label, penalty in substitutable_problem.penalties.items():  # type: ignore
        penalty_expr[pena_label] = penalty_substitute(penalty, instance_data, var_map)

    return CompiledInstance(
        sense=substitutable_problem.sense,
        objective=objective,
        constraint=constraint_expr,
        penalty=penalty_expr,
        var_map=var_map,
        data=instance_data,
        problem=substitutable_problem,
        deci_var_shape=eval_deci_var_shape(substitutable_problem, instance_data),
    )