From 922789693c700b45208a2d72f98def2dac96a4dc Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Mon, 18 Nov 2024 21:25:30 +0900 Subject: [PATCH 01/29] ParametricInstance and sub messages --- proto/ommx/v1/parametric_instance.proto | 54 +++++++++++++++++++++++++ 1 file changed, 54 insertions(+) create mode 100644 proto/ommx/v1/parametric_instance.proto diff --git a/proto/ommx/v1/parametric_instance.proto b/proto/ommx/v1/parametric_instance.proto new file mode 100644 index 00000000..633d1048 --- /dev/null +++ b/proto/ommx/v1/parametric_instance.proto @@ -0,0 +1,54 @@ +syntax = "proto3"; + +package ommx.v1; + +import "ommx/v1/constraint.proto"; +import "ommx/v1/decision_variables.proto"; +import "ommx/v1/function.proto"; +import "ommx/v1/instance.proto"; + +// Placeholder of a parameter in a parametrized optimization problem +message Parameter { + // ID for the parameter + // + // - IDs are not required to be sequential. + // - The ID must be unique within the instance including the decision variables. + uint64 id = 1; + + // Name of the parameter. e.g. `x` + optional string name = 2; + + // Subscripts of the parameter, same usage as DecisionVariable.subscripts + repeated int64 subscripts = 3; + + // Additional metadata for the parameter, same usage as DecisionVariable.parameters + map parameters = 4; + + // Human-readable description for the parameter + optional string description = 5; +} + +message Parameters { + map values = 1; +} + +message ParametricInstance { + Instance.Description description = 1; + + // Decision variables used in this instance + repeated DecisionVariable decision_variables = 2; + + // Parameters of this instance + // + // - The ID must be unique within the instance including the decision variables. + repeated Parameter parameters = 3; + + // Objective function of the optimization problem. This may contain parameters in addition to the decision variables. + Function objective = 4; + + // Constraints of the optimization problem. This may contain parameters in addition to the decision variables. + repeated Constraint constraints = 5; + + // The sense of this problem, i.e. minimize the objective or maximize it. + Instance.Sense sense = 6; +} From 38b8dc581b371c228916e884ffdacc014665e0f4 Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Tue, 19 Nov 2024 11:45:20 +0900 Subject: [PATCH 02/29] Rename `values` to `entries` in `Parameters` to match `State` message --- proto/ommx/v1/parametric_instance.proto | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/proto/ommx/v1/parametric_instance.proto b/proto/ommx/v1/parametric_instance.proto index 633d1048..21f89e34 100644 --- a/proto/ommx/v1/parametric_instance.proto +++ b/proto/ommx/v1/parametric_instance.proto @@ -28,8 +28,9 @@ message Parameter { optional string description = 5; } +// A set of parameters for instantiating an optimization problem from a parametric instance message Parameters { - map values = 1; + map entries = 1; } message ParametricInstance { From 421086dfa2f888d78dc6e99b668ade8237b03d32 Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Tue, 19 Nov 2024 21:02:29 +0900 Subject: [PATCH 03/29] Regenerate Rust and Python bindings --- .../ommx/ommx/v1/parametric_instance_pb2.py | 50 ++++ .../ommx/ommx/v1/parametric_instance_pb2.pyi | 245 ++++++++++++++++++ rust/ommx/src/ommx.v1.rs | 54 ++++ 3 files changed, 349 insertions(+) create mode 100644 python/ommx/ommx/v1/parametric_instance_pb2.py create mode 100644 python/ommx/ommx/v1/parametric_instance_pb2.pyi diff --git a/python/ommx/ommx/v1/parametric_instance_pb2.py b/python/ommx/ommx/v1/parametric_instance_pb2.py new file mode 100644 index 00000000..96844bb4 --- /dev/null +++ b/python/ommx/ommx/v1/parametric_instance_pb2.py @@ -0,0 +1,50 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: ommx/v1/parametric_instance.proto +# Protobuf Python Version: 5.26.1 +"""Generated protocol buffer code.""" + +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +from ommx.v1 import constraint_pb2 as ommx_dot_v1_dot_constraint__pb2 +from ommx.v1 import decision_variables_pb2 as ommx_dot_v1_dot_decision__variables__pb2 +from ommx.v1 import function_pb2 as ommx_dot_v1_dot_function__pb2 +from ommx.v1 import instance_pb2 as ommx_dot_v1_dot_instance__pb2 + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile( + b'\n!ommx/v1/parametric_instance.proto\x12\x07ommx.v1\x1a\x18ommx/v1/constraint.proto\x1a ommx/v1/decision_variables.proto\x1a\x16ommx/v1/function.proto\x1a\x16ommx/v1/instance.proto"\x97\x02\n\tParameter\x12\x0e\n\x02id\x18\x01 \x01(\x04R\x02id\x12\x17\n\x04name\x18\x02 \x01(\tH\x00R\x04name\x88\x01\x01\x12\x1e\n\nsubscripts\x18\x03 \x03(\x03R\nsubscripts\x12\x42\n\nparameters\x18\x04 \x03(\x0b\x32".ommx.v1.Parameter.ParametersEntryR\nparameters\x12%\n\x0b\x64\x65scription\x18\x05 \x01(\tH\x01R\x0b\x64\x65scription\x88\x01\x01\x1a=\n\x0fParametersEntry\x12\x10\n\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n\x05value\x18\x02 \x01(\tR\x05value:\x02\x38\x01\x42\x07\n\x05_nameB\x0e\n\x0c_description"\x84\x01\n\nParameters\x12:\n\x07\x65ntries\x18\x01 \x03(\x0b\x32 .ommx.v1.Parameters.EntriesEntryR\x07\x65ntries\x1a:\n\x0c\x45ntriesEntry\x12\x10\n\x03key\x18\x01 \x01(\x04R\x03key\x12\x14\n\x05value\x18\x02 \x01(\x01R\x05value:\x02\x38\x01"\xea\x02\n\x12ParametricInstance\x12?\n\x0b\x64\x65scription\x18\x01 \x01(\x0b\x32\x1d.ommx.v1.Instance.DescriptionR\x0b\x64\x65scription\x12H\n\x12\x64\x65\x63ision_variables\x18\x02 \x03(\x0b\x32\x19.ommx.v1.DecisionVariableR\x11\x64\x65\x63isionVariables\x12\x32\n\nparameters\x18\x03 \x03(\x0b\x32\x12.ommx.v1.ParameterR\nparameters\x12/\n\tobjective\x18\x04 \x01(\x0b\x32\x11.ommx.v1.FunctionR\tobjective\x12\x35\n\x0b\x63onstraints\x18\x05 \x03(\x0b\x32\x13.ommx.v1.ConstraintR\x0b\x63onstraints\x12-\n\x05sense\x18\x06 \x01(\x0e\x32\x17.ommx.v1.Instance.SenseR\x05senseBc\n\x0b\x63om.ommx.v1B\x17ParametricInstanceProtoP\x01\xa2\x02\x03OXX\xaa\x02\x07Ommx.V1\xca\x02\x07Ommx\\V1\xe2\x02\x13Ommx\\V1\\GPBMetadata\xea\x02\x08Ommx::V1b\x06proto3' +) + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages( + DESCRIPTOR, "ommx.v1.parametric_instance_pb2", _globals +) +if not _descriptor._USE_C_DESCRIPTORS: + _globals["DESCRIPTOR"]._loaded_options = None + _globals[ + "DESCRIPTOR" + ]._serialized_options = b"\n\013com.ommx.v1B\027ParametricInstanceProtoP\001\242\002\003OXX\252\002\007Ommx.V1\312\002\007Ommx\\V1\342\002\023Ommx\\V1\\GPBMetadata\352\002\010Ommx::V1" + _globals["_PARAMETER_PARAMETERSENTRY"]._loaded_options = None + _globals["_PARAMETER_PARAMETERSENTRY"]._serialized_options = b"8\001" + _globals["_PARAMETERS_ENTRIESENTRY"]._loaded_options = None + _globals["_PARAMETERS_ENTRIESENTRY"]._serialized_options = b"8\001" + _globals["_PARAMETER"]._serialized_start = 155 + _globals["_PARAMETER"]._serialized_end = 434 + _globals["_PARAMETER_PARAMETERSENTRY"]._serialized_start = 348 + _globals["_PARAMETER_PARAMETERSENTRY"]._serialized_end = 409 + _globals["_PARAMETERS"]._serialized_start = 437 + _globals["_PARAMETERS"]._serialized_end = 569 + _globals["_PARAMETERS_ENTRIESENTRY"]._serialized_start = 511 + _globals["_PARAMETERS_ENTRIESENTRY"]._serialized_end = 569 + _globals["_PARAMETRICINSTANCE"]._serialized_start = 572 + _globals["_PARAMETRICINSTANCE"]._serialized_end = 934 +# @@protoc_insertion_point(module_scope) diff --git a/python/ommx/ommx/v1/parametric_instance_pb2.pyi b/python/ommx/ommx/v1/parametric_instance_pb2.pyi new file mode 100644 index 00000000..ff369b57 --- /dev/null +++ b/python/ommx/ommx/v1/parametric_instance_pb2.pyi @@ -0,0 +1,245 @@ +""" +@generated by mypy-protobuf. Do not edit manually! +isort:skip_file +""" + +import builtins +import collections.abc +import google.protobuf.descriptor +import google.protobuf.internal.containers +import google.protobuf.message +import ommx.v1.constraint_pb2 +import ommx.v1.decision_variables_pb2 +import ommx.v1.function_pb2 +import ommx.v1.instance_pb2 +import typing + +DESCRIPTOR: google.protobuf.descriptor.FileDescriptor + +@typing.final +class Parameter(google.protobuf.message.Message): + """Placeholder of a parameter in a parametrized optimization problem""" + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + @typing.final + class ParametersEntry(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + KEY_FIELD_NUMBER: builtins.int + VALUE_FIELD_NUMBER: builtins.int + key: builtins.str + value: builtins.str + def __init__( + self, + *, + key: builtins.str = ..., + value: builtins.str = ..., + ) -> None: ... + def ClearField( + self, field_name: typing.Literal["key", b"key", "value", b"value"] + ) -> None: ... + + ID_FIELD_NUMBER: builtins.int + NAME_FIELD_NUMBER: builtins.int + SUBSCRIPTS_FIELD_NUMBER: builtins.int + PARAMETERS_FIELD_NUMBER: builtins.int + DESCRIPTION_FIELD_NUMBER: builtins.int + id: builtins.int + """ID for the parameter + + - IDs are not required to be sequential. + - The ID must be unique within the instance including the decision variables. + """ + name: builtins.str + """Name of the parameter. e.g. `x`""" + description: builtins.str + """Human-readable description for the parameter""" + @property + def subscripts( + self, + ) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.int]: + """Subscripts of the parameter, same usage as DecisionVariable.subscripts""" + + @property + def parameters( + self, + ) -> google.protobuf.internal.containers.ScalarMap[builtins.str, builtins.str]: + """Additional metadata for the parameter, same usage as DecisionVariable.parameters""" + + def __init__( + self, + *, + id: builtins.int = ..., + name: builtins.str | None = ..., + subscripts: collections.abc.Iterable[builtins.int] | None = ..., + parameters: collections.abc.Mapping[builtins.str, builtins.str] | None = ..., + description: builtins.str | None = ..., + ) -> None: ... + def HasField( + self, + field_name: typing.Literal[ + "_description", + b"_description", + "_name", + b"_name", + "description", + b"description", + "name", + b"name", + ], + ) -> builtins.bool: ... + def ClearField( + self, + field_name: typing.Literal[ + "_description", + b"_description", + "_name", + b"_name", + "description", + b"description", + "id", + b"id", + "name", + b"name", + "parameters", + b"parameters", + "subscripts", + b"subscripts", + ], + ) -> None: ... + @typing.overload + def WhichOneof( + self, oneof_group: typing.Literal["_description", b"_description"] + ) -> typing.Literal["description"] | None: ... + @typing.overload + def WhichOneof( + self, oneof_group: typing.Literal["_name", b"_name"] + ) -> typing.Literal["name"] | None: ... + +global___Parameter = Parameter + +@typing.final +class Parameters(google.protobuf.message.Message): + """A set of parameters for instantiating an optimization problem from a parametric instance""" + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + @typing.final + class EntriesEntry(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + KEY_FIELD_NUMBER: builtins.int + VALUE_FIELD_NUMBER: builtins.int + key: builtins.int + value: builtins.float + def __init__( + self, + *, + key: builtins.int = ..., + value: builtins.float = ..., + ) -> None: ... + def ClearField( + self, field_name: typing.Literal["key", b"key", "value", b"value"] + ) -> None: ... + + ENTRIES_FIELD_NUMBER: builtins.int + @property + def entries( + self, + ) -> google.protobuf.internal.containers.ScalarMap[ + builtins.int, builtins.float + ]: ... + def __init__( + self, + *, + entries: collections.abc.Mapping[builtins.int, builtins.float] | None = ..., + ) -> None: ... + def ClearField(self, field_name: typing.Literal["entries", b"entries"]) -> None: ... + +global___Parameters = Parameters + +@typing.final +class ParametricInstance(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + DESCRIPTION_FIELD_NUMBER: builtins.int + DECISION_VARIABLES_FIELD_NUMBER: builtins.int + PARAMETERS_FIELD_NUMBER: builtins.int + OBJECTIVE_FIELD_NUMBER: builtins.int + CONSTRAINTS_FIELD_NUMBER: builtins.int + SENSE_FIELD_NUMBER: builtins.int + sense: ommx.v1.instance_pb2.Instance.Sense.ValueType + """The sense of this problem, i.e. minimize the objective or maximize it.""" + @property + def description(self) -> ommx.v1.instance_pb2.Instance.Description: ... + @property + def decision_variables( + self, + ) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[ + ommx.v1.decision_variables_pb2.DecisionVariable + ]: + """Decision variables used in this instance""" + + @property + def parameters( + self, + ) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[ + global___Parameter + ]: + """Parameters of this instance + + - The ID must be unique within the instance including the decision variables. + """ + + @property + def objective(self) -> ommx.v1.function_pb2.Function: + """Objective function of the optimization problem. This may contain parameters in addition to the decision variables.""" + + @property + def constraints( + self, + ) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[ + ommx.v1.constraint_pb2.Constraint + ]: + """Constraints of the optimization problem. This may contain parameters in addition to the decision variables.""" + + def __init__( + self, + *, + description: ommx.v1.instance_pb2.Instance.Description | None = ..., + decision_variables: collections.abc.Iterable[ + ommx.v1.decision_variables_pb2.DecisionVariable + ] + | None = ..., + parameters: collections.abc.Iterable[global___Parameter] | None = ..., + objective: ommx.v1.function_pb2.Function | None = ..., + constraints: collections.abc.Iterable[ommx.v1.constraint_pb2.Constraint] + | None = ..., + sense: ommx.v1.instance_pb2.Instance.Sense.ValueType = ..., + ) -> None: ... + def HasField( + self, + field_name: typing.Literal[ + "description", b"description", "objective", b"objective" + ], + ) -> builtins.bool: ... + def ClearField( + self, + field_name: typing.Literal[ + "constraints", + b"constraints", + "decision_variables", + b"decision_variables", + "description", + b"description", + "objective", + b"objective", + "parameters", + b"parameters", + "sense", + b"sense", + ], + ) -> None: ... + +global___ParametricInstance = ParametricInstance diff --git a/rust/ommx/src/ommx.v1.rs b/rust/ommx/src/ommx.v1.rs index 89130217..2077eb32 100644 --- a/rust/ommx/src/ommx.v1.rs +++ b/rust/ommx/src/ommx.v1.rs @@ -346,6 +346,60 @@ pub mod instance { } } } +/// Placeholder of a parameter in a parametrized optimization problem +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct Parameter { + /// ID for the parameter + /// + /// - IDs are not required to be sequential. + /// - The ID must be unique within the instance including the decision variables. + #[prost(uint64, tag = "1")] + pub id: u64, + /// Name of the parameter. e.g. `x` + #[prost(string, optional, tag = "2")] + pub name: ::core::option::Option<::prost::alloc::string::String>, + /// Subscripts of the parameter, same usage as DecisionVariable.subscripts + #[prost(int64, repeated, tag = "3")] + pub subscripts: ::prost::alloc::vec::Vec, + /// Additional metadata for the parameter, same usage as DecisionVariable.parameters + #[prost(map = "string, string", tag = "4")] + pub parameters: + ::std::collections::HashMap<::prost::alloc::string::String, ::prost::alloc::string::String>, + /// Human-readable description for the parameter + #[prost(string, optional, tag = "5")] + pub description: ::core::option::Option<::prost::alloc::string::String>, +} +/// A set of parameters for instantiating an optimization problem from a parametric instance +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct Parameters { + #[prost(map = "uint64, double", tag = "1")] + pub entries: ::std::collections::HashMap, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct ParametricInstance { + #[prost(message, optional, tag = "1")] + pub description: ::core::option::Option, + /// Decision variables used in this instance + #[prost(message, repeated, tag = "2")] + pub decision_variables: ::prost::alloc::vec::Vec, + /// Parameters of this instance + /// + /// - The ID must be unique within the instance including the decision variables. + #[prost(message, repeated, tag = "3")] + pub parameters: ::prost::alloc::vec::Vec, + /// Objective function of the optimization problem. This may contain parameters in addition to the decision variables. + #[prost(message, optional, tag = "4")] + pub objective: ::core::option::Option, + /// Constraints of the optimization problem. This may contain parameters in addition to the decision variables. + #[prost(message, repeated, tag = "5")] + pub constraints: ::prost::alloc::vec::Vec, + /// The sense of this problem, i.e. minimize the objective or maximize it. + #[prost(enumeration = "instance::Sense", tag = "6")] + pub sense: i32, +} /// A set of values of decision variables, without any evaluation, even the /// feasiblity of the solution. #[allow(clippy::derive_partial_eq_without_eq)] From 540012fa8cb771e2b616526958ad1851e7ba50b6 Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Tue, 19 Nov 2024 21:15:55 +0900 Subject: [PATCH 04/29] Split parameter.proto --- proto/ommx/v1/instance.proto | 4 + proto/ommx/v1/parameter.proto | 29 ++++ proto/ommx/v1/parametric_instance.proto | 29 +--- python/ommx/ommx/v1/instance_pb2.py | 15 +- python/ommx/ommx/v1/instance_pb2.pyi | 23 ++- python/ommx/ommx/v1/parameter_pb2.py | 40 +++++ python/ommx/ommx/v1/parameter_pb2.pyi | 156 ++++++++++++++++++ .../ommx/ommx/v1/parametric_instance_pb2.py | 19 +-- .../ommx/ommx/v1/parametric_instance_pb2.pyi | 151 +---------------- rust/ommx/src/ommx.v1.rs | 67 ++++---- 10 files changed, 309 insertions(+), 224 deletions(-) create mode 100644 proto/ommx/v1/parameter.proto create mode 100644 python/ommx/ommx/v1/parameter_pb2.py create mode 100644 python/ommx/ommx/v1/parameter_pb2.pyi diff --git a/proto/ommx/v1/instance.proto b/proto/ommx/v1/instance.proto index 9066aaa2..4bfdd5fb 100644 --- a/proto/ommx/v1/instance.proto +++ b/proto/ommx/v1/instance.proto @@ -5,6 +5,7 @@ package ommx.v1; import "ommx/v1/constraint.proto"; import "ommx/v1/decision_variables.proto"; import "ommx/v1/function.proto"; +import "ommx/v1/parameter.proto"; message Instance { message Description { @@ -45,4 +46,7 @@ message Instance { // - This is a required field. Most mathematical modeling tools allow for an empty sense and default to minimization. Alternatively, some tools do not create such a field and represent maximization problems by negating the objective function. This project prefers explicit descriptions over implicit ones to avoid such ambiguity and to make it unnecessary for developers to look up the reference for the treatment of omitted cases. // Sense sense = 5; + + // Parameters used when instantiating this instance + optional Parameters parameters = 6; } diff --git a/proto/ommx/v1/parameter.proto b/proto/ommx/v1/parameter.proto new file mode 100644 index 00000000..d93ec1b2 --- /dev/null +++ b/proto/ommx/v1/parameter.proto @@ -0,0 +1,29 @@ +syntax = "proto3"; + +package ommx.v1; + +// Placeholder of a parameter in a parametrized optimization problem +message Parameter { + // ID for the parameter + // + // - IDs are not required to be sequential. + // - The ID must be unique within the instance including the decision variables. + uint64 id = 1; + + // Name of the parameter. e.g. `x` + optional string name = 2; + + // Subscripts of the parameter, same usage as DecisionVariable.subscripts + repeated int64 subscripts = 3; + + // Additional metadata for the parameter, same usage as DecisionVariable.parameters + map parameters = 4; + + // Human-readable description for the parameter + optional string description = 5; +} + +// A set of parameters for instantiating an optimization problem from a parametric instance +message Parameters { + map entries = 1; +} diff --git a/proto/ommx/v1/parametric_instance.proto b/proto/ommx/v1/parametric_instance.proto index 21f89e34..438a3b9e 100644 --- a/proto/ommx/v1/parametric_instance.proto +++ b/proto/ommx/v1/parametric_instance.proto @@ -6,33 +6,10 @@ import "ommx/v1/constraint.proto"; import "ommx/v1/decision_variables.proto"; import "ommx/v1/function.proto"; import "ommx/v1/instance.proto"; +import "ommx/v1/parameter.proto"; -// Placeholder of a parameter in a parametrized optimization problem -message Parameter { - // ID for the parameter - // - // - IDs are not required to be sequential. - // - The ID must be unique within the instance including the decision variables. - uint64 id = 1; - - // Name of the parameter. e.g. `x` - optional string name = 2; - - // Subscripts of the parameter, same usage as DecisionVariable.subscripts - repeated int64 subscripts = 3; - - // Additional metadata for the parameter, same usage as DecisionVariable.parameters - map parameters = 4; - - // Human-readable description for the parameter - optional string description = 5; -} - -// A set of parameters for instantiating an optimization problem from a parametric instance -message Parameters { - map entries = 1; -} - +// Optimization problem including parameter, variables varying while solving the problem like penalty weights or dual variables. +// These parameters are not decision variables. message ParametricInstance { Instance.Description description = 1; diff --git a/python/ommx/ommx/v1/instance_pb2.py b/python/ommx/ommx/v1/instance_pb2.py index 831ddc61..52af514c 100644 --- a/python/ommx/ommx/v1/instance_pb2.py +++ b/python/ommx/ommx/v1/instance_pb2.py @@ -16,10 +16,11 @@ from ommx.v1 import constraint_pb2 as ommx_dot_v1_dot_constraint__pb2 from ommx.v1 import decision_variables_pb2 as ommx_dot_v1_dot_decision__variables__pb2 from ommx.v1 import function_pb2 as ommx_dot_v1_dot_function__pb2 +from ommx.v1 import parameter_pb2 as ommx_dot_v1_dot_parameter__pb2 DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile( - b'\n\x16ommx/v1/instance.proto\x12\x07ommx.v1\x1a\x18ommx/v1/constraint.proto\x1a ommx/v1/decision_variables.proto\x1a\x16ommx/v1/function.proto"\xaa\x04\n\x08Instance\x12?\n\x0b\x64\x65scription\x18\x01 \x01(\x0b\x32\x1d.ommx.v1.Instance.DescriptionR\x0b\x64\x65scription\x12H\n\x12\x64\x65\x63ision_variables\x18\x02 \x03(\x0b\x32\x19.ommx.v1.DecisionVariableR\x11\x64\x65\x63isionVariables\x12/\n\tobjective\x18\x03 \x01(\x0b\x32\x11.ommx.v1.FunctionR\tobjective\x12\x35\n\x0b\x63onstraints\x18\x04 \x03(\x0b\x32\x13.ommx.v1.ConstraintR\x0b\x63onstraints\x12-\n\x05sense\x18\x05 \x01(\x0e\x32\x17.ommx.v1.Instance.SenseR\x05sense\x1a\xb3\x01\n\x0b\x44\x65scription\x12\x17\n\x04name\x18\x01 \x01(\tH\x00R\x04name\x88\x01\x01\x12%\n\x0b\x64\x65scription\x18\x02 \x01(\tH\x01R\x0b\x64\x65scription\x88\x01\x01\x12\x18\n\x07\x61uthors\x18\x03 \x03(\tR\x07\x61uthors\x12"\n\ncreated_by\x18\x04 \x01(\tH\x02R\tcreatedBy\x88\x01\x01\x42\x07\n\x05_nameB\x0e\n\x0c_descriptionB\r\n\x0b_created_by"F\n\x05Sense\x12\x15\n\x11SENSE_UNSPECIFIED\x10\x00\x12\x12\n\x0eSENSE_MINIMIZE\x10\x01\x12\x12\n\x0eSENSE_MAXIMIZE\x10\x02\x42Y\n\x0b\x63om.ommx.v1B\rInstanceProtoP\x01\xa2\x02\x03OXX\xaa\x02\x07Ommx.V1\xca\x02\x07Ommx\\V1\xe2\x02\x13Ommx\\V1\\GPBMetadata\xea\x02\x08Ommx::V1b\x06proto3' + b'\n\x16ommx/v1/instance.proto\x12\x07ommx.v1\x1a\x18ommx/v1/constraint.proto\x1a ommx/v1/decision_variables.proto\x1a\x16ommx/v1/function.proto\x1a\x17ommx/v1/parameter.proto"\xf3\x04\n\x08Instance\x12?\n\x0b\x64\x65scription\x18\x01 \x01(\x0b\x32\x1d.ommx.v1.Instance.DescriptionR\x0b\x64\x65scription\x12H\n\x12\x64\x65\x63ision_variables\x18\x02 \x03(\x0b\x32\x19.ommx.v1.DecisionVariableR\x11\x64\x65\x63isionVariables\x12/\n\tobjective\x18\x03 \x01(\x0b\x32\x11.ommx.v1.FunctionR\tobjective\x12\x35\n\x0b\x63onstraints\x18\x04 \x03(\x0b\x32\x13.ommx.v1.ConstraintR\x0b\x63onstraints\x12-\n\x05sense\x18\x05 \x01(\x0e\x32\x17.ommx.v1.Instance.SenseR\x05sense\x12\x38\n\nparameters\x18\x06 \x01(\x0b\x32\x13.ommx.v1.ParametersH\x00R\nparameters\x88\x01\x01\x1a\xb3\x01\n\x0b\x44\x65scription\x12\x17\n\x04name\x18\x01 \x01(\tH\x00R\x04name\x88\x01\x01\x12%\n\x0b\x64\x65scription\x18\x02 \x01(\tH\x01R\x0b\x64\x65scription\x88\x01\x01\x12\x18\n\x07\x61uthors\x18\x03 \x03(\tR\x07\x61uthors\x12"\n\ncreated_by\x18\x04 \x01(\tH\x02R\tcreatedBy\x88\x01\x01\x42\x07\n\x05_nameB\x0e\n\x0c_descriptionB\r\n\x0b_created_by"F\n\x05Sense\x12\x15\n\x11SENSE_UNSPECIFIED\x10\x00\x12\x12\n\x0eSENSE_MINIMIZE\x10\x01\x12\x12\n\x0eSENSE_MAXIMIZE\x10\x02\x42\r\n\x0b_parametersBY\n\x0b\x63om.ommx.v1B\rInstanceProtoP\x01\xa2\x02\x03OXX\xaa\x02\x07Ommx.V1\xca\x02\x07Ommx\\V1\xe2\x02\x13Ommx\\V1\\GPBMetadata\xea\x02\x08Ommx::V1b\x06proto3' ) _globals = globals() @@ -30,10 +31,10 @@ _globals[ "DESCRIPTOR" ]._serialized_options = b"\n\013com.ommx.v1B\rInstanceProtoP\001\242\002\003OXX\252\002\007Ommx.V1\312\002\007Ommx\\V1\342\002\023Ommx\\V1\\GPBMetadata\352\002\010Ommx::V1" - _globals["_INSTANCE"]._serialized_start = 120 - _globals["_INSTANCE"]._serialized_end = 674 - _globals["_INSTANCE_DESCRIPTION"]._serialized_start = 423 - _globals["_INSTANCE_DESCRIPTION"]._serialized_end = 602 - _globals["_INSTANCE_SENSE"]._serialized_start = 604 - _globals["_INSTANCE_SENSE"]._serialized_end = 674 + _globals["_INSTANCE"]._serialized_start = 145 + _globals["_INSTANCE"]._serialized_end = 772 + _globals["_INSTANCE_DESCRIPTION"]._serialized_start = 506 + _globals["_INSTANCE_DESCRIPTION"]._serialized_end = 685 + _globals["_INSTANCE_SENSE"]._serialized_start = 687 + _globals["_INSTANCE_SENSE"]._serialized_end = 757 # @@protoc_insertion_point(module_scope) diff --git a/python/ommx/ommx/v1/instance_pb2.pyi b/python/ommx/ommx/v1/instance_pb2.pyi index c8a21b55..825d37c6 100644 --- a/python/ommx/ommx/v1/instance_pb2.pyi +++ b/python/ommx/ommx/v1/instance_pb2.pyi @@ -12,6 +12,7 @@ import google.protobuf.message import ommx.v1.constraint_pb2 import ommx.v1.decision_variables_pb2 import ommx.v1.function_pb2 +import ommx.v1.parameter_pb2 import sys import typing @@ -134,6 +135,7 @@ class Instance(google.protobuf.message.Message): OBJECTIVE_FIELD_NUMBER: builtins.int CONSTRAINTS_FIELD_NUMBER: builtins.int SENSE_FIELD_NUMBER: builtins.int + PARAMETERS_FIELD_NUMBER: builtins.int sense: global___Instance.Sense.ValueType """The sense of this problem, i.e. minimize the objective or maximize it. @@ -164,6 +166,10 @@ class Instance(google.protobuf.message.Message): ]: """Constraints of the optimization problem""" + @property + def parameters(self) -> ommx.v1.parameter_pb2.Parameters: + """Parameters used when instantiating this instance""" + def __init__( self, *, @@ -176,16 +182,26 @@ class Instance(google.protobuf.message.Message): constraints: collections.abc.Iterable[ommx.v1.constraint_pb2.Constraint] | None = ..., sense: global___Instance.Sense.ValueType = ..., + parameters: ommx.v1.parameter_pb2.Parameters | None = ..., ) -> None: ... def HasField( self, field_name: typing.Literal[ - "description", b"description", "objective", b"objective" + "_parameters", + b"_parameters", + "description", + b"description", + "objective", + b"objective", + "parameters", + b"parameters", ], ) -> builtins.bool: ... def ClearField( self, field_name: typing.Literal[ + "_parameters", + b"_parameters", "constraints", b"constraints", "decision_variables", @@ -194,9 +210,14 @@ class Instance(google.protobuf.message.Message): b"description", "objective", b"objective", + "parameters", + b"parameters", "sense", b"sense", ], ) -> None: ... + def WhichOneof( + self, oneof_group: typing.Literal["_parameters", b"_parameters"] + ) -> typing.Literal["parameters"] | None: ... global___Instance = Instance diff --git a/python/ommx/ommx/v1/parameter_pb2.py b/python/ommx/ommx/v1/parameter_pb2.py new file mode 100644 index 00000000..b0730f4c --- /dev/null +++ b/python/ommx/ommx/v1/parameter_pb2.py @@ -0,0 +1,40 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: ommx/v1/parameter.proto +# Protobuf Python Version: 5.26.1 +"""Generated protocol buffer code.""" + +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile( + b'\n\x17ommx/v1/parameter.proto\x12\x07ommx.v1"\x97\x02\n\tParameter\x12\x0e\n\x02id\x18\x01 \x01(\x04R\x02id\x12\x17\n\x04name\x18\x02 \x01(\tH\x00R\x04name\x88\x01\x01\x12\x1e\n\nsubscripts\x18\x03 \x03(\x03R\nsubscripts\x12\x42\n\nparameters\x18\x04 \x03(\x0b\x32".ommx.v1.Parameter.ParametersEntryR\nparameters\x12%\n\x0b\x64\x65scription\x18\x05 \x01(\tH\x01R\x0b\x64\x65scription\x88\x01\x01\x1a=\n\x0fParametersEntry\x12\x10\n\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n\x05value\x18\x02 \x01(\tR\x05value:\x02\x38\x01\x42\x07\n\x05_nameB\x0e\n\x0c_description"\x84\x01\n\nParameters\x12:\n\x07\x65ntries\x18\x01 \x03(\x0b\x32 .ommx.v1.Parameters.EntriesEntryR\x07\x65ntries\x1a:\n\x0c\x45ntriesEntry\x12\x10\n\x03key\x18\x01 \x01(\x04R\x03key\x12\x14\n\x05value\x18\x02 \x01(\x01R\x05value:\x02\x38\x01\x42Z\n\x0b\x63om.ommx.v1B\x0eParameterProtoP\x01\xa2\x02\x03OXX\xaa\x02\x07Ommx.V1\xca\x02\x07Ommx\\V1\xe2\x02\x13Ommx\\V1\\GPBMetadata\xea\x02\x08Ommx::V1b\x06proto3' +) + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, "ommx.v1.parameter_pb2", _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals["DESCRIPTOR"]._loaded_options = None + _globals[ + "DESCRIPTOR" + ]._serialized_options = b"\n\013com.ommx.v1B\016ParameterProtoP\001\242\002\003OXX\252\002\007Ommx.V1\312\002\007Ommx\\V1\342\002\023Ommx\\V1\\GPBMetadata\352\002\010Ommx::V1" + _globals["_PARAMETER_PARAMETERSENTRY"]._loaded_options = None + _globals["_PARAMETER_PARAMETERSENTRY"]._serialized_options = b"8\001" + _globals["_PARAMETERS_ENTRIESENTRY"]._loaded_options = None + _globals["_PARAMETERS_ENTRIESENTRY"]._serialized_options = b"8\001" + _globals["_PARAMETER"]._serialized_start = 37 + _globals["_PARAMETER"]._serialized_end = 316 + _globals["_PARAMETER_PARAMETERSENTRY"]._serialized_start = 230 + _globals["_PARAMETER_PARAMETERSENTRY"]._serialized_end = 291 + _globals["_PARAMETERS"]._serialized_start = 319 + _globals["_PARAMETERS"]._serialized_end = 451 + _globals["_PARAMETERS_ENTRIESENTRY"]._serialized_start = 393 + _globals["_PARAMETERS_ENTRIESENTRY"]._serialized_end = 451 +# @@protoc_insertion_point(module_scope) diff --git a/python/ommx/ommx/v1/parameter_pb2.pyi b/python/ommx/ommx/v1/parameter_pb2.pyi new file mode 100644 index 00000000..1516b98f --- /dev/null +++ b/python/ommx/ommx/v1/parameter_pb2.pyi @@ -0,0 +1,156 @@ +""" +@generated by mypy-protobuf. Do not edit manually! +isort:skip_file +""" + +import builtins +import collections.abc +import google.protobuf.descriptor +import google.protobuf.internal.containers +import google.protobuf.message +import typing + +DESCRIPTOR: google.protobuf.descriptor.FileDescriptor + +@typing.final +class Parameter(google.protobuf.message.Message): + """Placeholder of a parameter in a parametrized optimization problem""" + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + @typing.final + class ParametersEntry(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + KEY_FIELD_NUMBER: builtins.int + VALUE_FIELD_NUMBER: builtins.int + key: builtins.str + value: builtins.str + def __init__( + self, + *, + key: builtins.str = ..., + value: builtins.str = ..., + ) -> None: ... + def ClearField( + self, field_name: typing.Literal["key", b"key", "value", b"value"] + ) -> None: ... + + ID_FIELD_NUMBER: builtins.int + NAME_FIELD_NUMBER: builtins.int + SUBSCRIPTS_FIELD_NUMBER: builtins.int + PARAMETERS_FIELD_NUMBER: builtins.int + DESCRIPTION_FIELD_NUMBER: builtins.int + id: builtins.int + """ID for the parameter + + - IDs are not required to be sequential. + - The ID must be unique within the instance including the decision variables. + """ + name: builtins.str + """Name of the parameter. e.g. `x`""" + description: builtins.str + """Human-readable description for the parameter""" + @property + def subscripts( + self, + ) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.int]: + """Subscripts of the parameter, same usage as DecisionVariable.subscripts""" + + @property + def parameters( + self, + ) -> google.protobuf.internal.containers.ScalarMap[builtins.str, builtins.str]: + """Additional metadata for the parameter, same usage as DecisionVariable.parameters""" + + def __init__( + self, + *, + id: builtins.int = ..., + name: builtins.str | None = ..., + subscripts: collections.abc.Iterable[builtins.int] | None = ..., + parameters: collections.abc.Mapping[builtins.str, builtins.str] | None = ..., + description: builtins.str | None = ..., + ) -> None: ... + def HasField( + self, + field_name: typing.Literal[ + "_description", + b"_description", + "_name", + b"_name", + "description", + b"description", + "name", + b"name", + ], + ) -> builtins.bool: ... + def ClearField( + self, + field_name: typing.Literal[ + "_description", + b"_description", + "_name", + b"_name", + "description", + b"description", + "id", + b"id", + "name", + b"name", + "parameters", + b"parameters", + "subscripts", + b"subscripts", + ], + ) -> None: ... + @typing.overload + def WhichOneof( + self, oneof_group: typing.Literal["_description", b"_description"] + ) -> typing.Literal["description"] | None: ... + @typing.overload + def WhichOneof( + self, oneof_group: typing.Literal["_name", b"_name"] + ) -> typing.Literal["name"] | None: ... + +global___Parameter = Parameter + +@typing.final +class Parameters(google.protobuf.message.Message): + """A set of parameters for instantiating an optimization problem from a parametric instance""" + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + @typing.final + class EntriesEntry(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + KEY_FIELD_NUMBER: builtins.int + VALUE_FIELD_NUMBER: builtins.int + key: builtins.int + value: builtins.float + def __init__( + self, + *, + key: builtins.int = ..., + value: builtins.float = ..., + ) -> None: ... + def ClearField( + self, field_name: typing.Literal["key", b"key", "value", b"value"] + ) -> None: ... + + ENTRIES_FIELD_NUMBER: builtins.int + @property + def entries( + self, + ) -> google.protobuf.internal.containers.ScalarMap[ + builtins.int, builtins.float + ]: ... + def __init__( + self, + *, + entries: collections.abc.Mapping[builtins.int, builtins.float] | None = ..., + ) -> None: ... + def ClearField(self, field_name: typing.Literal["entries", b"entries"]) -> None: ... + +global___Parameters = Parameters diff --git a/python/ommx/ommx/v1/parametric_instance_pb2.py b/python/ommx/ommx/v1/parametric_instance_pb2.py index 96844bb4..798bd6c4 100644 --- a/python/ommx/ommx/v1/parametric_instance_pb2.py +++ b/python/ommx/ommx/v1/parametric_instance_pb2.py @@ -17,10 +17,11 @@ from ommx.v1 import decision_variables_pb2 as ommx_dot_v1_dot_decision__variables__pb2 from ommx.v1 import function_pb2 as ommx_dot_v1_dot_function__pb2 from ommx.v1 import instance_pb2 as ommx_dot_v1_dot_instance__pb2 +from ommx.v1 import parameter_pb2 as ommx_dot_v1_dot_parameter__pb2 DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile( - b'\n!ommx/v1/parametric_instance.proto\x12\x07ommx.v1\x1a\x18ommx/v1/constraint.proto\x1a ommx/v1/decision_variables.proto\x1a\x16ommx/v1/function.proto\x1a\x16ommx/v1/instance.proto"\x97\x02\n\tParameter\x12\x0e\n\x02id\x18\x01 \x01(\x04R\x02id\x12\x17\n\x04name\x18\x02 \x01(\tH\x00R\x04name\x88\x01\x01\x12\x1e\n\nsubscripts\x18\x03 \x03(\x03R\nsubscripts\x12\x42\n\nparameters\x18\x04 \x03(\x0b\x32".ommx.v1.Parameter.ParametersEntryR\nparameters\x12%\n\x0b\x64\x65scription\x18\x05 \x01(\tH\x01R\x0b\x64\x65scription\x88\x01\x01\x1a=\n\x0fParametersEntry\x12\x10\n\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n\x05value\x18\x02 \x01(\tR\x05value:\x02\x38\x01\x42\x07\n\x05_nameB\x0e\n\x0c_description"\x84\x01\n\nParameters\x12:\n\x07\x65ntries\x18\x01 \x03(\x0b\x32 .ommx.v1.Parameters.EntriesEntryR\x07\x65ntries\x1a:\n\x0c\x45ntriesEntry\x12\x10\n\x03key\x18\x01 \x01(\x04R\x03key\x12\x14\n\x05value\x18\x02 \x01(\x01R\x05value:\x02\x38\x01"\xea\x02\n\x12ParametricInstance\x12?\n\x0b\x64\x65scription\x18\x01 \x01(\x0b\x32\x1d.ommx.v1.Instance.DescriptionR\x0b\x64\x65scription\x12H\n\x12\x64\x65\x63ision_variables\x18\x02 \x03(\x0b\x32\x19.ommx.v1.DecisionVariableR\x11\x64\x65\x63isionVariables\x12\x32\n\nparameters\x18\x03 \x03(\x0b\x32\x12.ommx.v1.ParameterR\nparameters\x12/\n\tobjective\x18\x04 \x01(\x0b\x32\x11.ommx.v1.FunctionR\tobjective\x12\x35\n\x0b\x63onstraints\x18\x05 \x03(\x0b\x32\x13.ommx.v1.ConstraintR\x0b\x63onstraints\x12-\n\x05sense\x18\x06 \x01(\x0e\x32\x17.ommx.v1.Instance.SenseR\x05senseBc\n\x0b\x63om.ommx.v1B\x17ParametricInstanceProtoP\x01\xa2\x02\x03OXX\xaa\x02\x07Ommx.V1\xca\x02\x07Ommx\\V1\xe2\x02\x13Ommx\\V1\\GPBMetadata\xea\x02\x08Ommx::V1b\x06proto3' + b'\n!ommx/v1/parametric_instance.proto\x12\x07ommx.v1\x1a\x18ommx/v1/constraint.proto\x1a ommx/v1/decision_variables.proto\x1a\x16ommx/v1/function.proto\x1a\x16ommx/v1/instance.proto\x1a\x17ommx/v1/parameter.proto"\xea\x02\n\x12ParametricInstance\x12?\n\x0b\x64\x65scription\x18\x01 \x01(\x0b\x32\x1d.ommx.v1.Instance.DescriptionR\x0b\x64\x65scription\x12H\n\x12\x64\x65\x63ision_variables\x18\x02 \x03(\x0b\x32\x19.ommx.v1.DecisionVariableR\x11\x64\x65\x63isionVariables\x12\x32\n\nparameters\x18\x03 \x03(\x0b\x32\x12.ommx.v1.ParameterR\nparameters\x12/\n\tobjective\x18\x04 \x01(\x0b\x32\x11.ommx.v1.FunctionR\tobjective\x12\x35\n\x0b\x63onstraints\x18\x05 \x03(\x0b\x32\x13.ommx.v1.ConstraintR\x0b\x63onstraints\x12-\n\x05sense\x18\x06 \x01(\x0e\x32\x17.ommx.v1.Instance.SenseR\x05senseBc\n\x0b\x63om.ommx.v1B\x17ParametricInstanceProtoP\x01\xa2\x02\x03OXX\xaa\x02\x07Ommx.V1\xca\x02\x07Ommx\\V1\xe2\x02\x13Ommx\\V1\\GPBMetadata\xea\x02\x08Ommx::V1b\x06proto3' ) _globals = globals() @@ -33,18 +34,6 @@ _globals[ "DESCRIPTOR" ]._serialized_options = b"\n\013com.ommx.v1B\027ParametricInstanceProtoP\001\242\002\003OXX\252\002\007Ommx.V1\312\002\007Ommx\\V1\342\002\023Ommx\\V1\\GPBMetadata\352\002\010Ommx::V1" - _globals["_PARAMETER_PARAMETERSENTRY"]._loaded_options = None - _globals["_PARAMETER_PARAMETERSENTRY"]._serialized_options = b"8\001" - _globals["_PARAMETERS_ENTRIESENTRY"]._loaded_options = None - _globals["_PARAMETERS_ENTRIESENTRY"]._serialized_options = b"8\001" - _globals["_PARAMETER"]._serialized_start = 155 - _globals["_PARAMETER"]._serialized_end = 434 - _globals["_PARAMETER_PARAMETERSENTRY"]._serialized_start = 348 - _globals["_PARAMETER_PARAMETERSENTRY"]._serialized_end = 409 - _globals["_PARAMETERS"]._serialized_start = 437 - _globals["_PARAMETERS"]._serialized_end = 569 - _globals["_PARAMETERS_ENTRIESENTRY"]._serialized_start = 511 - _globals["_PARAMETERS_ENTRIESENTRY"]._serialized_end = 569 - _globals["_PARAMETRICINSTANCE"]._serialized_start = 572 - _globals["_PARAMETRICINSTANCE"]._serialized_end = 934 + _globals["_PARAMETRICINSTANCE"]._serialized_start = 180 + _globals["_PARAMETRICINSTANCE"]._serialized_end = 542 # @@protoc_insertion_point(module_scope) diff --git a/python/ommx/ommx/v1/parametric_instance_pb2.pyi b/python/ommx/ommx/v1/parametric_instance_pb2.pyi index ff369b57..48783562 100644 --- a/python/ommx/ommx/v1/parametric_instance_pb2.pyi +++ b/python/ommx/ommx/v1/parametric_instance_pb2.pyi @@ -12,157 +12,19 @@ import ommx.v1.constraint_pb2 import ommx.v1.decision_variables_pb2 import ommx.v1.function_pb2 import ommx.v1.instance_pb2 +import ommx.v1.parameter_pb2 import typing DESCRIPTOR: google.protobuf.descriptor.FileDescriptor @typing.final -class Parameter(google.protobuf.message.Message): - """Placeholder of a parameter in a parametrized optimization problem""" - - DESCRIPTOR: google.protobuf.descriptor.Descriptor - - @typing.final - class ParametersEntry(google.protobuf.message.Message): - DESCRIPTOR: google.protobuf.descriptor.Descriptor - - KEY_FIELD_NUMBER: builtins.int - VALUE_FIELD_NUMBER: builtins.int - key: builtins.str - value: builtins.str - def __init__( - self, - *, - key: builtins.str = ..., - value: builtins.str = ..., - ) -> None: ... - def ClearField( - self, field_name: typing.Literal["key", b"key", "value", b"value"] - ) -> None: ... - - ID_FIELD_NUMBER: builtins.int - NAME_FIELD_NUMBER: builtins.int - SUBSCRIPTS_FIELD_NUMBER: builtins.int - PARAMETERS_FIELD_NUMBER: builtins.int - DESCRIPTION_FIELD_NUMBER: builtins.int - id: builtins.int - """ID for the parameter - - - IDs are not required to be sequential. - - The ID must be unique within the instance including the decision variables. +class ParametricInstance(google.protobuf.message.Message): + """Optimization problem including parameter, variables varying while solving the problem like penalty weights or dual variables. + These parameters are not decision variables. """ - name: builtins.str - """Name of the parameter. e.g. `x`""" - description: builtins.str - """Human-readable description for the parameter""" - @property - def subscripts( - self, - ) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.int]: - """Subscripts of the parameter, same usage as DecisionVariable.subscripts""" - - @property - def parameters( - self, - ) -> google.protobuf.internal.containers.ScalarMap[builtins.str, builtins.str]: - """Additional metadata for the parameter, same usage as DecisionVariable.parameters""" - - def __init__( - self, - *, - id: builtins.int = ..., - name: builtins.str | None = ..., - subscripts: collections.abc.Iterable[builtins.int] | None = ..., - parameters: collections.abc.Mapping[builtins.str, builtins.str] | None = ..., - description: builtins.str | None = ..., - ) -> None: ... - def HasField( - self, - field_name: typing.Literal[ - "_description", - b"_description", - "_name", - b"_name", - "description", - b"description", - "name", - b"name", - ], - ) -> builtins.bool: ... - def ClearField( - self, - field_name: typing.Literal[ - "_description", - b"_description", - "_name", - b"_name", - "description", - b"description", - "id", - b"id", - "name", - b"name", - "parameters", - b"parameters", - "subscripts", - b"subscripts", - ], - ) -> None: ... - @typing.overload - def WhichOneof( - self, oneof_group: typing.Literal["_description", b"_description"] - ) -> typing.Literal["description"] | None: ... - @typing.overload - def WhichOneof( - self, oneof_group: typing.Literal["_name", b"_name"] - ) -> typing.Literal["name"] | None: ... - -global___Parameter = Parameter - -@typing.final -class Parameters(google.protobuf.message.Message): - """A set of parameters for instantiating an optimization problem from a parametric instance""" DESCRIPTOR: google.protobuf.descriptor.Descriptor - @typing.final - class EntriesEntry(google.protobuf.message.Message): - DESCRIPTOR: google.protobuf.descriptor.Descriptor - - KEY_FIELD_NUMBER: builtins.int - VALUE_FIELD_NUMBER: builtins.int - key: builtins.int - value: builtins.float - def __init__( - self, - *, - key: builtins.int = ..., - value: builtins.float = ..., - ) -> None: ... - def ClearField( - self, field_name: typing.Literal["key", b"key", "value", b"value"] - ) -> None: ... - - ENTRIES_FIELD_NUMBER: builtins.int - @property - def entries( - self, - ) -> google.protobuf.internal.containers.ScalarMap[ - builtins.int, builtins.float - ]: ... - def __init__( - self, - *, - entries: collections.abc.Mapping[builtins.int, builtins.float] | None = ..., - ) -> None: ... - def ClearField(self, field_name: typing.Literal["entries", b"entries"]) -> None: ... - -global___Parameters = Parameters - -@typing.final -class ParametricInstance(google.protobuf.message.Message): - DESCRIPTOR: google.protobuf.descriptor.Descriptor - DESCRIPTION_FIELD_NUMBER: builtins.int DECISION_VARIABLES_FIELD_NUMBER: builtins.int PARAMETERS_FIELD_NUMBER: builtins.int @@ -185,7 +47,7 @@ class ParametricInstance(google.protobuf.message.Message): def parameters( self, ) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[ - global___Parameter + ommx.v1.parameter_pb2.Parameter ]: """Parameters of this instance @@ -212,7 +74,8 @@ class ParametricInstance(google.protobuf.message.Message): ommx.v1.decision_variables_pb2.DecisionVariable ] | None = ..., - parameters: collections.abc.Iterable[global___Parameter] | None = ..., + parameters: collections.abc.Iterable[ommx.v1.parameter_pb2.Parameter] + | None = ..., objective: ommx.v1.function_pb2.Function | None = ..., constraints: collections.abc.Iterable[ommx.v1.constraint_pb2.Constraint] | None = ..., diff --git a/rust/ommx/src/ommx.v1.rs b/rust/ommx/src/ommx.v1.rs index 2077eb32..d984b7ad 100644 --- a/rust/ommx/src/ommx.v1.rs +++ b/rust/ommx/src/ommx.v1.rs @@ -276,6 +276,37 @@ pub mod decision_variable { } } } +/// Placeholder of a parameter in a parametrized optimization problem +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct Parameter { + /// ID for the parameter + /// + /// - IDs are not required to be sequential. + /// - The ID must be unique within the instance including the decision variables. + #[prost(uint64, tag = "1")] + pub id: u64, + /// Name of the parameter. e.g. `x` + #[prost(string, optional, tag = "2")] + pub name: ::core::option::Option<::prost::alloc::string::String>, + /// Subscripts of the parameter, same usage as DecisionVariable.subscripts + #[prost(int64, repeated, tag = "3")] + pub subscripts: ::prost::alloc::vec::Vec, + /// Additional metadata for the parameter, same usage as DecisionVariable.parameters + #[prost(map = "string, string", tag = "4")] + pub parameters: + ::std::collections::HashMap<::prost::alloc::string::String, ::prost::alloc::string::String>, + /// Human-readable description for the parameter + #[prost(string, optional, tag = "5")] + pub description: ::core::option::Option<::prost::alloc::string::String>, +} +/// A set of parameters for instantiating an optimization problem from a parametric instance +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct Parameters { + #[prost(map = "uint64, double", tag = "1")] + pub entries: ::std::collections::HashMap, +} #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct Instance { @@ -299,6 +330,9 @@ pub struct Instance { /// #[prost(enumeration = "instance::Sense", tag = "5")] pub sense: i32, + /// Parameters used when instantiating this instance + #[prost(message, optional, tag = "6")] + pub parameters: ::core::option::Option, } /// Nested message and enum types in `Instance`. pub mod instance { @@ -346,37 +380,8 @@ pub mod instance { } } } -/// Placeholder of a parameter in a parametrized optimization problem -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct Parameter { - /// ID for the parameter - /// - /// - IDs are not required to be sequential. - /// - The ID must be unique within the instance including the decision variables. - #[prost(uint64, tag = "1")] - pub id: u64, - /// Name of the parameter. e.g. `x` - #[prost(string, optional, tag = "2")] - pub name: ::core::option::Option<::prost::alloc::string::String>, - /// Subscripts of the parameter, same usage as DecisionVariable.subscripts - #[prost(int64, repeated, tag = "3")] - pub subscripts: ::prost::alloc::vec::Vec, - /// Additional metadata for the parameter, same usage as DecisionVariable.parameters - #[prost(map = "string, string", tag = "4")] - pub parameters: - ::std::collections::HashMap<::prost::alloc::string::String, ::prost::alloc::string::String>, - /// Human-readable description for the parameter - #[prost(string, optional, tag = "5")] - pub description: ::core::option::Option<::prost::alloc::string::String>, -} -/// A set of parameters for instantiating an optimization problem from a parametric instance -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct Parameters { - #[prost(map = "uint64, double", tag = "1")] - pub entries: ::std::collections::HashMap, -} +/// Optimization problem including parameter, variables varying while solving the problem like penalty weights or dual variables. +/// These parameters are not decision variables. #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct ParametricInstance { From 356595fc080448dec3443f6bcbda1c21d6769dae Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Tue, 19 Nov 2024 21:17:40 +0900 Subject: [PATCH 05/29] Move Parameters to instance.proto --- proto/ommx/v1/instance.proto | 6 +++- proto/ommx/v1/parameter.proto | 5 --- python/ommx/ommx/v1/instance_pb2.py | 21 +++++++----- python/ommx/ommx/v1/instance_pb2.pyi | 45 +++++++++++++++++++++++-- python/ommx/ommx/v1/parameter_pb2.py | 8 +---- python/ommx/ommx/v1/parameter_pb2.pyi | 40 ---------------------- rust/ommx/src/ommx.v1.rs | 48 +++++++++++++-------------- 7 files changed, 85 insertions(+), 88 deletions(-) diff --git a/proto/ommx/v1/instance.proto b/proto/ommx/v1/instance.proto index 4bfdd5fb..5ca08b52 100644 --- a/proto/ommx/v1/instance.proto +++ b/proto/ommx/v1/instance.proto @@ -5,7 +5,11 @@ package ommx.v1; import "ommx/v1/constraint.proto"; import "ommx/v1/decision_variables.proto"; import "ommx/v1/function.proto"; -import "ommx/v1/parameter.proto"; + +// A set of parameters for instantiating an optimization problem from a parametric instance +message Parameters { + map entries = 1; +} message Instance { message Description { diff --git a/proto/ommx/v1/parameter.proto b/proto/ommx/v1/parameter.proto index d93ec1b2..c811f59c 100644 --- a/proto/ommx/v1/parameter.proto +++ b/proto/ommx/v1/parameter.proto @@ -22,8 +22,3 @@ message Parameter { // Human-readable description for the parameter optional string description = 5; } - -// A set of parameters for instantiating an optimization problem from a parametric instance -message Parameters { - map entries = 1; -} diff --git a/python/ommx/ommx/v1/instance_pb2.py b/python/ommx/ommx/v1/instance_pb2.py index 52af514c..9b1df2d0 100644 --- a/python/ommx/ommx/v1/instance_pb2.py +++ b/python/ommx/ommx/v1/instance_pb2.py @@ -16,11 +16,10 @@ from ommx.v1 import constraint_pb2 as ommx_dot_v1_dot_constraint__pb2 from ommx.v1 import decision_variables_pb2 as ommx_dot_v1_dot_decision__variables__pb2 from ommx.v1 import function_pb2 as ommx_dot_v1_dot_function__pb2 -from ommx.v1 import parameter_pb2 as ommx_dot_v1_dot_parameter__pb2 DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile( - b'\n\x16ommx/v1/instance.proto\x12\x07ommx.v1\x1a\x18ommx/v1/constraint.proto\x1a ommx/v1/decision_variables.proto\x1a\x16ommx/v1/function.proto\x1a\x17ommx/v1/parameter.proto"\xf3\x04\n\x08Instance\x12?\n\x0b\x64\x65scription\x18\x01 \x01(\x0b\x32\x1d.ommx.v1.Instance.DescriptionR\x0b\x64\x65scription\x12H\n\x12\x64\x65\x63ision_variables\x18\x02 \x03(\x0b\x32\x19.ommx.v1.DecisionVariableR\x11\x64\x65\x63isionVariables\x12/\n\tobjective\x18\x03 \x01(\x0b\x32\x11.ommx.v1.FunctionR\tobjective\x12\x35\n\x0b\x63onstraints\x18\x04 \x03(\x0b\x32\x13.ommx.v1.ConstraintR\x0b\x63onstraints\x12-\n\x05sense\x18\x05 \x01(\x0e\x32\x17.ommx.v1.Instance.SenseR\x05sense\x12\x38\n\nparameters\x18\x06 \x01(\x0b\x32\x13.ommx.v1.ParametersH\x00R\nparameters\x88\x01\x01\x1a\xb3\x01\n\x0b\x44\x65scription\x12\x17\n\x04name\x18\x01 \x01(\tH\x00R\x04name\x88\x01\x01\x12%\n\x0b\x64\x65scription\x18\x02 \x01(\tH\x01R\x0b\x64\x65scription\x88\x01\x01\x12\x18\n\x07\x61uthors\x18\x03 \x03(\tR\x07\x61uthors\x12"\n\ncreated_by\x18\x04 \x01(\tH\x02R\tcreatedBy\x88\x01\x01\x42\x07\n\x05_nameB\x0e\n\x0c_descriptionB\r\n\x0b_created_by"F\n\x05Sense\x12\x15\n\x11SENSE_UNSPECIFIED\x10\x00\x12\x12\n\x0eSENSE_MINIMIZE\x10\x01\x12\x12\n\x0eSENSE_MAXIMIZE\x10\x02\x42\r\n\x0b_parametersBY\n\x0b\x63om.ommx.v1B\rInstanceProtoP\x01\xa2\x02\x03OXX\xaa\x02\x07Ommx.V1\xca\x02\x07Ommx\\V1\xe2\x02\x13Ommx\\V1\\GPBMetadata\xea\x02\x08Ommx::V1b\x06proto3' + b'\n\x16ommx/v1/instance.proto\x12\x07ommx.v1\x1a\x18ommx/v1/constraint.proto\x1a ommx/v1/decision_variables.proto\x1a\x16ommx/v1/function.proto"\x84\x01\n\nParameters\x12:\n\x07\x65ntries\x18\x01 \x03(\x0b\x32 .ommx.v1.Parameters.EntriesEntryR\x07\x65ntries\x1a:\n\x0c\x45ntriesEntry\x12\x10\n\x03key\x18\x01 \x01(\x04R\x03key\x12\x14\n\x05value\x18\x02 \x01(\x01R\x05value:\x02\x38\x01"\xf3\x04\n\x08Instance\x12?\n\x0b\x64\x65scription\x18\x01 \x01(\x0b\x32\x1d.ommx.v1.Instance.DescriptionR\x0b\x64\x65scription\x12H\n\x12\x64\x65\x63ision_variables\x18\x02 \x03(\x0b\x32\x19.ommx.v1.DecisionVariableR\x11\x64\x65\x63isionVariables\x12/\n\tobjective\x18\x03 \x01(\x0b\x32\x11.ommx.v1.FunctionR\tobjective\x12\x35\n\x0b\x63onstraints\x18\x04 \x03(\x0b\x32\x13.ommx.v1.ConstraintR\x0b\x63onstraints\x12-\n\x05sense\x18\x05 \x01(\x0e\x32\x17.ommx.v1.Instance.SenseR\x05sense\x12\x38\n\nparameters\x18\x06 \x01(\x0b\x32\x13.ommx.v1.ParametersH\x00R\nparameters\x88\x01\x01\x1a\xb3\x01\n\x0b\x44\x65scription\x12\x17\n\x04name\x18\x01 \x01(\tH\x00R\x04name\x88\x01\x01\x12%\n\x0b\x64\x65scription\x18\x02 \x01(\tH\x01R\x0b\x64\x65scription\x88\x01\x01\x12\x18\n\x07\x61uthors\x18\x03 \x03(\tR\x07\x61uthors\x12"\n\ncreated_by\x18\x04 \x01(\tH\x02R\tcreatedBy\x88\x01\x01\x42\x07\n\x05_nameB\x0e\n\x0c_descriptionB\r\n\x0b_created_by"F\n\x05Sense\x12\x15\n\x11SENSE_UNSPECIFIED\x10\x00\x12\x12\n\x0eSENSE_MINIMIZE\x10\x01\x12\x12\n\x0eSENSE_MAXIMIZE\x10\x02\x42\r\n\x0b_parametersBY\n\x0b\x63om.ommx.v1B\rInstanceProtoP\x01\xa2\x02\x03OXX\xaa\x02\x07Ommx.V1\xca\x02\x07Ommx\\V1\xe2\x02\x13Ommx\\V1\\GPBMetadata\xea\x02\x08Ommx::V1b\x06proto3' ) _globals = globals() @@ -31,10 +30,16 @@ _globals[ "DESCRIPTOR" ]._serialized_options = b"\n\013com.ommx.v1B\rInstanceProtoP\001\242\002\003OXX\252\002\007Ommx.V1\312\002\007Ommx\\V1\342\002\023Ommx\\V1\\GPBMetadata\352\002\010Ommx::V1" - _globals["_INSTANCE"]._serialized_start = 145 - _globals["_INSTANCE"]._serialized_end = 772 - _globals["_INSTANCE_DESCRIPTION"]._serialized_start = 506 - _globals["_INSTANCE_DESCRIPTION"]._serialized_end = 685 - _globals["_INSTANCE_SENSE"]._serialized_start = 687 - _globals["_INSTANCE_SENSE"]._serialized_end = 757 + _globals["_PARAMETERS_ENTRIESENTRY"]._loaded_options = None + _globals["_PARAMETERS_ENTRIESENTRY"]._serialized_options = b"8\001" + _globals["_PARAMETERS"]._serialized_start = 120 + _globals["_PARAMETERS"]._serialized_end = 252 + _globals["_PARAMETERS_ENTRIESENTRY"]._serialized_start = 194 + _globals["_PARAMETERS_ENTRIESENTRY"]._serialized_end = 252 + _globals["_INSTANCE"]._serialized_start = 255 + _globals["_INSTANCE"]._serialized_end = 882 + _globals["_INSTANCE_DESCRIPTION"]._serialized_start = 616 + _globals["_INSTANCE_DESCRIPTION"]._serialized_end = 795 + _globals["_INSTANCE_SENSE"]._serialized_start = 797 + _globals["_INSTANCE_SENSE"]._serialized_end = 867 # @@protoc_insertion_point(module_scope) diff --git a/python/ommx/ommx/v1/instance_pb2.pyi b/python/ommx/ommx/v1/instance_pb2.pyi index 825d37c6..f47a47ea 100644 --- a/python/ommx/ommx/v1/instance_pb2.pyi +++ b/python/ommx/ommx/v1/instance_pb2.pyi @@ -12,7 +12,6 @@ import google.protobuf.message import ommx.v1.constraint_pb2 import ommx.v1.decision_variables_pb2 import ommx.v1.function_pb2 -import ommx.v1.parameter_pb2 import sys import typing @@ -23,6 +22,46 @@ else: DESCRIPTOR: google.protobuf.descriptor.FileDescriptor +@typing.final +class Parameters(google.protobuf.message.Message): + """A set of parameters for instantiating an optimization problem from a parametric instance""" + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + @typing.final + class EntriesEntry(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + KEY_FIELD_NUMBER: builtins.int + VALUE_FIELD_NUMBER: builtins.int + key: builtins.int + value: builtins.float + def __init__( + self, + *, + key: builtins.int = ..., + value: builtins.float = ..., + ) -> None: ... + def ClearField( + self, field_name: typing.Literal["key", b"key", "value", b"value"] + ) -> None: ... + + ENTRIES_FIELD_NUMBER: builtins.int + @property + def entries( + self, + ) -> google.protobuf.internal.containers.ScalarMap[ + builtins.int, builtins.float + ]: ... + def __init__( + self, + *, + entries: collections.abc.Mapping[builtins.int, builtins.float] | None = ..., + ) -> None: ... + def ClearField(self, field_name: typing.Literal["entries", b"entries"]) -> None: ... + +global___Parameters = Parameters + @typing.final class Instance(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -167,7 +206,7 @@ class Instance(google.protobuf.message.Message): """Constraints of the optimization problem""" @property - def parameters(self) -> ommx.v1.parameter_pb2.Parameters: + def parameters(self) -> global___Parameters: """Parameters used when instantiating this instance""" def __init__( @@ -182,7 +221,7 @@ class Instance(google.protobuf.message.Message): constraints: collections.abc.Iterable[ommx.v1.constraint_pb2.Constraint] | None = ..., sense: global___Instance.Sense.ValueType = ..., - parameters: ommx.v1.parameter_pb2.Parameters | None = ..., + parameters: global___Parameters | None = ..., ) -> None: ... def HasField( self, diff --git a/python/ommx/ommx/v1/parameter_pb2.py b/python/ommx/ommx/v1/parameter_pb2.py index b0730f4c..f485606a 100644 --- a/python/ommx/ommx/v1/parameter_pb2.py +++ b/python/ommx/ommx/v1/parameter_pb2.py @@ -14,7 +14,7 @@ DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile( - b'\n\x17ommx/v1/parameter.proto\x12\x07ommx.v1"\x97\x02\n\tParameter\x12\x0e\n\x02id\x18\x01 \x01(\x04R\x02id\x12\x17\n\x04name\x18\x02 \x01(\tH\x00R\x04name\x88\x01\x01\x12\x1e\n\nsubscripts\x18\x03 \x03(\x03R\nsubscripts\x12\x42\n\nparameters\x18\x04 \x03(\x0b\x32".ommx.v1.Parameter.ParametersEntryR\nparameters\x12%\n\x0b\x64\x65scription\x18\x05 \x01(\tH\x01R\x0b\x64\x65scription\x88\x01\x01\x1a=\n\x0fParametersEntry\x12\x10\n\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n\x05value\x18\x02 \x01(\tR\x05value:\x02\x38\x01\x42\x07\n\x05_nameB\x0e\n\x0c_description"\x84\x01\n\nParameters\x12:\n\x07\x65ntries\x18\x01 \x03(\x0b\x32 .ommx.v1.Parameters.EntriesEntryR\x07\x65ntries\x1a:\n\x0c\x45ntriesEntry\x12\x10\n\x03key\x18\x01 \x01(\x04R\x03key\x12\x14\n\x05value\x18\x02 \x01(\x01R\x05value:\x02\x38\x01\x42Z\n\x0b\x63om.ommx.v1B\x0eParameterProtoP\x01\xa2\x02\x03OXX\xaa\x02\x07Ommx.V1\xca\x02\x07Ommx\\V1\xe2\x02\x13Ommx\\V1\\GPBMetadata\xea\x02\x08Ommx::V1b\x06proto3' + b'\n\x17ommx/v1/parameter.proto\x12\x07ommx.v1"\x97\x02\n\tParameter\x12\x0e\n\x02id\x18\x01 \x01(\x04R\x02id\x12\x17\n\x04name\x18\x02 \x01(\tH\x00R\x04name\x88\x01\x01\x12\x1e\n\nsubscripts\x18\x03 \x03(\x03R\nsubscripts\x12\x42\n\nparameters\x18\x04 \x03(\x0b\x32".ommx.v1.Parameter.ParametersEntryR\nparameters\x12%\n\x0b\x64\x65scription\x18\x05 \x01(\tH\x01R\x0b\x64\x65scription\x88\x01\x01\x1a=\n\x0fParametersEntry\x12\x10\n\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n\x05value\x18\x02 \x01(\tR\x05value:\x02\x38\x01\x42\x07\n\x05_nameB\x0e\n\x0c_descriptionBZ\n\x0b\x63om.ommx.v1B\x0eParameterProtoP\x01\xa2\x02\x03OXX\xaa\x02\x07Ommx.V1\xca\x02\x07Ommx\\V1\xe2\x02\x13Ommx\\V1\\GPBMetadata\xea\x02\x08Ommx::V1b\x06proto3' ) _globals = globals() @@ -27,14 +27,8 @@ ]._serialized_options = b"\n\013com.ommx.v1B\016ParameterProtoP\001\242\002\003OXX\252\002\007Ommx.V1\312\002\007Ommx\\V1\342\002\023Ommx\\V1\\GPBMetadata\352\002\010Ommx::V1" _globals["_PARAMETER_PARAMETERSENTRY"]._loaded_options = None _globals["_PARAMETER_PARAMETERSENTRY"]._serialized_options = b"8\001" - _globals["_PARAMETERS_ENTRIESENTRY"]._loaded_options = None - _globals["_PARAMETERS_ENTRIESENTRY"]._serialized_options = b"8\001" _globals["_PARAMETER"]._serialized_start = 37 _globals["_PARAMETER"]._serialized_end = 316 _globals["_PARAMETER_PARAMETERSENTRY"]._serialized_start = 230 _globals["_PARAMETER_PARAMETERSENTRY"]._serialized_end = 291 - _globals["_PARAMETERS"]._serialized_start = 319 - _globals["_PARAMETERS"]._serialized_end = 451 - _globals["_PARAMETERS_ENTRIESENTRY"]._serialized_start = 393 - _globals["_PARAMETERS_ENTRIESENTRY"]._serialized_end = 451 # @@protoc_insertion_point(module_scope) diff --git a/python/ommx/ommx/v1/parameter_pb2.pyi b/python/ommx/ommx/v1/parameter_pb2.pyi index 1516b98f..04e27310 100644 --- a/python/ommx/ommx/v1/parameter_pb2.pyi +++ b/python/ommx/ommx/v1/parameter_pb2.pyi @@ -114,43 +114,3 @@ class Parameter(google.protobuf.message.Message): ) -> typing.Literal["name"] | None: ... global___Parameter = Parameter - -@typing.final -class Parameters(google.protobuf.message.Message): - """A set of parameters for instantiating an optimization problem from a parametric instance""" - - DESCRIPTOR: google.protobuf.descriptor.Descriptor - - @typing.final - class EntriesEntry(google.protobuf.message.Message): - DESCRIPTOR: google.protobuf.descriptor.Descriptor - - KEY_FIELD_NUMBER: builtins.int - VALUE_FIELD_NUMBER: builtins.int - key: builtins.int - value: builtins.float - def __init__( - self, - *, - key: builtins.int = ..., - value: builtins.float = ..., - ) -> None: ... - def ClearField( - self, field_name: typing.Literal["key", b"key", "value", b"value"] - ) -> None: ... - - ENTRIES_FIELD_NUMBER: builtins.int - @property - def entries( - self, - ) -> google.protobuf.internal.containers.ScalarMap[ - builtins.int, builtins.float - ]: ... - def __init__( - self, - *, - entries: collections.abc.Mapping[builtins.int, builtins.float] | None = ..., - ) -> None: ... - def ClearField(self, field_name: typing.Literal["entries", b"entries"]) -> None: ... - -global___Parameters = Parameters diff --git a/rust/ommx/src/ommx.v1.rs b/rust/ommx/src/ommx.v1.rs index d984b7ad..19bd52dd 100644 --- a/rust/ommx/src/ommx.v1.rs +++ b/rust/ommx/src/ommx.v1.rs @@ -276,30 +276,6 @@ pub mod decision_variable { } } } -/// Placeholder of a parameter in a parametrized optimization problem -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct Parameter { - /// ID for the parameter - /// - /// - IDs are not required to be sequential. - /// - The ID must be unique within the instance including the decision variables. - #[prost(uint64, tag = "1")] - pub id: u64, - /// Name of the parameter. e.g. `x` - #[prost(string, optional, tag = "2")] - pub name: ::core::option::Option<::prost::alloc::string::String>, - /// Subscripts of the parameter, same usage as DecisionVariable.subscripts - #[prost(int64, repeated, tag = "3")] - pub subscripts: ::prost::alloc::vec::Vec, - /// Additional metadata for the parameter, same usage as DecisionVariable.parameters - #[prost(map = "string, string", tag = "4")] - pub parameters: - ::std::collections::HashMap<::prost::alloc::string::String, ::prost::alloc::string::String>, - /// Human-readable description for the parameter - #[prost(string, optional, tag = "5")] - pub description: ::core::option::Option<::prost::alloc::string::String>, -} /// A set of parameters for instantiating an optimization problem from a parametric instance #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] @@ -380,6 +356,30 @@ pub mod instance { } } } +/// Placeholder of a parameter in a parametrized optimization problem +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct Parameter { + /// ID for the parameter + /// + /// - IDs are not required to be sequential. + /// - The ID must be unique within the instance including the decision variables. + #[prost(uint64, tag = "1")] + pub id: u64, + /// Name of the parameter. e.g. `x` + #[prost(string, optional, tag = "2")] + pub name: ::core::option::Option<::prost::alloc::string::String>, + /// Subscripts of the parameter, same usage as DecisionVariable.subscripts + #[prost(int64, repeated, tag = "3")] + pub subscripts: ::prost::alloc::vec::Vec, + /// Additional metadata for the parameter, same usage as DecisionVariable.parameters + #[prost(map = "string, string", tag = "4")] + pub parameters: + ::std::collections::HashMap<::prost::alloc::string::String, ::prost::alloc::string::String>, + /// Human-readable description for the parameter + #[prost(string, optional, tag = "5")] + pub description: ::core::option::Option<::prost::alloc::string::String>, +} /// Optimization problem including parameter, variables varying while solving the problem like penalty weights or dual variables. /// These parameters are not decision variables. #[allow(clippy::derive_partial_eq_without_eq)] From e3e64ccdf500a22cc5ac2317e6c197f8e72bea43 Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Tue, 19 Nov 2024 21:19:02 +0900 Subject: [PATCH 06/29] Re-merge --- proto/ommx/v1/parameter.proto | 24 ---- proto/ommx/v1/parametric_instance.proto | 22 +++- .../ommx/ommx/v1/parametric_instance_pb2.py | 13 ++- .../ommx/ommx/v1/parametric_instance_pb2.pyi | 109 +++++++++++++++++- 4 files changed, 135 insertions(+), 33 deletions(-) delete mode 100644 proto/ommx/v1/parameter.proto diff --git a/proto/ommx/v1/parameter.proto b/proto/ommx/v1/parameter.proto deleted file mode 100644 index c811f59c..00000000 --- a/proto/ommx/v1/parameter.proto +++ /dev/null @@ -1,24 +0,0 @@ -syntax = "proto3"; - -package ommx.v1; - -// Placeholder of a parameter in a parametrized optimization problem -message Parameter { - // ID for the parameter - // - // - IDs are not required to be sequential. - // - The ID must be unique within the instance including the decision variables. - uint64 id = 1; - - // Name of the parameter. e.g. `x` - optional string name = 2; - - // Subscripts of the parameter, same usage as DecisionVariable.subscripts - repeated int64 subscripts = 3; - - // Additional metadata for the parameter, same usage as DecisionVariable.parameters - map parameters = 4; - - // Human-readable description for the parameter - optional string description = 5; -} diff --git a/proto/ommx/v1/parametric_instance.proto b/proto/ommx/v1/parametric_instance.proto index 438a3b9e..0c3a0dd7 100644 --- a/proto/ommx/v1/parametric_instance.proto +++ b/proto/ommx/v1/parametric_instance.proto @@ -6,7 +6,27 @@ import "ommx/v1/constraint.proto"; import "ommx/v1/decision_variables.proto"; import "ommx/v1/function.proto"; import "ommx/v1/instance.proto"; -import "ommx/v1/parameter.proto"; + +// Placeholder of a parameter in a parametrized optimization problem +message Parameter { + // ID for the parameter + // + // - IDs are not required to be sequential. + // - The ID must be unique within the instance including the decision variables. + uint64 id = 1; + + // Name of the parameter. e.g. `x` + optional string name = 2; + + // Subscripts of the parameter, same usage as DecisionVariable.subscripts + repeated int64 subscripts = 3; + + // Additional metadata for the parameter, same usage as DecisionVariable.parameters + map parameters = 4; + + // Human-readable description for the parameter + optional string description = 5; +} // Optimization problem including parameter, variables varying while solving the problem like penalty weights or dual variables. // These parameters are not decision variables. diff --git a/python/ommx/ommx/v1/parametric_instance_pb2.py b/python/ommx/ommx/v1/parametric_instance_pb2.py index 798bd6c4..485c5571 100644 --- a/python/ommx/ommx/v1/parametric_instance_pb2.py +++ b/python/ommx/ommx/v1/parametric_instance_pb2.py @@ -17,11 +17,10 @@ from ommx.v1 import decision_variables_pb2 as ommx_dot_v1_dot_decision__variables__pb2 from ommx.v1 import function_pb2 as ommx_dot_v1_dot_function__pb2 from ommx.v1 import instance_pb2 as ommx_dot_v1_dot_instance__pb2 -from ommx.v1 import parameter_pb2 as ommx_dot_v1_dot_parameter__pb2 DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile( - b'\n!ommx/v1/parametric_instance.proto\x12\x07ommx.v1\x1a\x18ommx/v1/constraint.proto\x1a ommx/v1/decision_variables.proto\x1a\x16ommx/v1/function.proto\x1a\x16ommx/v1/instance.proto\x1a\x17ommx/v1/parameter.proto"\xea\x02\n\x12ParametricInstance\x12?\n\x0b\x64\x65scription\x18\x01 \x01(\x0b\x32\x1d.ommx.v1.Instance.DescriptionR\x0b\x64\x65scription\x12H\n\x12\x64\x65\x63ision_variables\x18\x02 \x03(\x0b\x32\x19.ommx.v1.DecisionVariableR\x11\x64\x65\x63isionVariables\x12\x32\n\nparameters\x18\x03 \x03(\x0b\x32\x12.ommx.v1.ParameterR\nparameters\x12/\n\tobjective\x18\x04 \x01(\x0b\x32\x11.ommx.v1.FunctionR\tobjective\x12\x35\n\x0b\x63onstraints\x18\x05 \x03(\x0b\x32\x13.ommx.v1.ConstraintR\x0b\x63onstraints\x12-\n\x05sense\x18\x06 \x01(\x0e\x32\x17.ommx.v1.Instance.SenseR\x05senseBc\n\x0b\x63om.ommx.v1B\x17ParametricInstanceProtoP\x01\xa2\x02\x03OXX\xaa\x02\x07Ommx.V1\xca\x02\x07Ommx\\V1\xe2\x02\x13Ommx\\V1\\GPBMetadata\xea\x02\x08Ommx::V1b\x06proto3' + b'\n!ommx/v1/parametric_instance.proto\x12\x07ommx.v1\x1a\x18ommx/v1/constraint.proto\x1a ommx/v1/decision_variables.proto\x1a\x16ommx/v1/function.proto\x1a\x16ommx/v1/instance.proto"\x97\x02\n\tParameter\x12\x0e\n\x02id\x18\x01 \x01(\x04R\x02id\x12\x17\n\x04name\x18\x02 \x01(\tH\x00R\x04name\x88\x01\x01\x12\x1e\n\nsubscripts\x18\x03 \x03(\x03R\nsubscripts\x12\x42\n\nparameters\x18\x04 \x03(\x0b\x32".ommx.v1.Parameter.ParametersEntryR\nparameters\x12%\n\x0b\x64\x65scription\x18\x05 \x01(\tH\x01R\x0b\x64\x65scription\x88\x01\x01\x1a=\n\x0fParametersEntry\x12\x10\n\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n\x05value\x18\x02 \x01(\tR\x05value:\x02\x38\x01\x42\x07\n\x05_nameB\x0e\n\x0c_description"\xea\x02\n\x12ParametricInstance\x12?\n\x0b\x64\x65scription\x18\x01 \x01(\x0b\x32\x1d.ommx.v1.Instance.DescriptionR\x0b\x64\x65scription\x12H\n\x12\x64\x65\x63ision_variables\x18\x02 \x03(\x0b\x32\x19.ommx.v1.DecisionVariableR\x11\x64\x65\x63isionVariables\x12\x32\n\nparameters\x18\x03 \x03(\x0b\x32\x12.ommx.v1.ParameterR\nparameters\x12/\n\tobjective\x18\x04 \x01(\x0b\x32\x11.ommx.v1.FunctionR\tobjective\x12\x35\n\x0b\x63onstraints\x18\x05 \x03(\x0b\x32\x13.ommx.v1.ConstraintR\x0b\x63onstraints\x12-\n\x05sense\x18\x06 \x01(\x0e\x32\x17.ommx.v1.Instance.SenseR\x05senseBc\n\x0b\x63om.ommx.v1B\x17ParametricInstanceProtoP\x01\xa2\x02\x03OXX\xaa\x02\x07Ommx.V1\xca\x02\x07Ommx\\V1\xe2\x02\x13Ommx\\V1\\GPBMetadata\xea\x02\x08Ommx::V1b\x06proto3' ) _globals = globals() @@ -34,6 +33,12 @@ _globals[ "DESCRIPTOR" ]._serialized_options = b"\n\013com.ommx.v1B\027ParametricInstanceProtoP\001\242\002\003OXX\252\002\007Ommx.V1\312\002\007Ommx\\V1\342\002\023Ommx\\V1\\GPBMetadata\352\002\010Ommx::V1" - _globals["_PARAMETRICINSTANCE"]._serialized_start = 180 - _globals["_PARAMETRICINSTANCE"]._serialized_end = 542 + _globals["_PARAMETER_PARAMETERSENTRY"]._loaded_options = None + _globals["_PARAMETER_PARAMETERSENTRY"]._serialized_options = b"8\001" + _globals["_PARAMETER"]._serialized_start = 155 + _globals["_PARAMETER"]._serialized_end = 434 + _globals["_PARAMETER_PARAMETERSENTRY"]._serialized_start = 348 + _globals["_PARAMETER_PARAMETERSENTRY"]._serialized_end = 409 + _globals["_PARAMETRICINSTANCE"]._serialized_start = 437 + _globals["_PARAMETRICINSTANCE"]._serialized_end = 799 # @@protoc_insertion_point(module_scope) diff --git a/python/ommx/ommx/v1/parametric_instance_pb2.pyi b/python/ommx/ommx/v1/parametric_instance_pb2.pyi index 48783562..0513df87 100644 --- a/python/ommx/ommx/v1/parametric_instance_pb2.pyi +++ b/python/ommx/ommx/v1/parametric_instance_pb2.pyi @@ -12,11 +12,113 @@ import ommx.v1.constraint_pb2 import ommx.v1.decision_variables_pb2 import ommx.v1.function_pb2 import ommx.v1.instance_pb2 -import ommx.v1.parameter_pb2 import typing DESCRIPTOR: google.protobuf.descriptor.FileDescriptor +@typing.final +class Parameter(google.protobuf.message.Message): + """Placeholder of a parameter in a parametrized optimization problem""" + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + @typing.final + class ParametersEntry(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + KEY_FIELD_NUMBER: builtins.int + VALUE_FIELD_NUMBER: builtins.int + key: builtins.str + value: builtins.str + def __init__( + self, + *, + key: builtins.str = ..., + value: builtins.str = ..., + ) -> None: ... + def ClearField( + self, field_name: typing.Literal["key", b"key", "value", b"value"] + ) -> None: ... + + ID_FIELD_NUMBER: builtins.int + NAME_FIELD_NUMBER: builtins.int + SUBSCRIPTS_FIELD_NUMBER: builtins.int + PARAMETERS_FIELD_NUMBER: builtins.int + DESCRIPTION_FIELD_NUMBER: builtins.int + id: builtins.int + """ID for the parameter + + - IDs are not required to be sequential. + - The ID must be unique within the instance including the decision variables. + """ + name: builtins.str + """Name of the parameter. e.g. `x`""" + description: builtins.str + """Human-readable description for the parameter""" + @property + def subscripts( + self, + ) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.int]: + """Subscripts of the parameter, same usage as DecisionVariable.subscripts""" + + @property + def parameters( + self, + ) -> google.protobuf.internal.containers.ScalarMap[builtins.str, builtins.str]: + """Additional metadata for the parameter, same usage as DecisionVariable.parameters""" + + def __init__( + self, + *, + id: builtins.int = ..., + name: builtins.str | None = ..., + subscripts: collections.abc.Iterable[builtins.int] | None = ..., + parameters: collections.abc.Mapping[builtins.str, builtins.str] | None = ..., + description: builtins.str | None = ..., + ) -> None: ... + def HasField( + self, + field_name: typing.Literal[ + "_description", + b"_description", + "_name", + b"_name", + "description", + b"description", + "name", + b"name", + ], + ) -> builtins.bool: ... + def ClearField( + self, + field_name: typing.Literal[ + "_description", + b"_description", + "_name", + b"_name", + "description", + b"description", + "id", + b"id", + "name", + b"name", + "parameters", + b"parameters", + "subscripts", + b"subscripts", + ], + ) -> None: ... + @typing.overload + def WhichOneof( + self, oneof_group: typing.Literal["_description", b"_description"] + ) -> typing.Literal["description"] | None: ... + @typing.overload + def WhichOneof( + self, oneof_group: typing.Literal["_name", b"_name"] + ) -> typing.Literal["name"] | None: ... + +global___Parameter = Parameter + @typing.final class ParametricInstance(google.protobuf.message.Message): """Optimization problem including parameter, variables varying while solving the problem like penalty weights or dual variables. @@ -47,7 +149,7 @@ class ParametricInstance(google.protobuf.message.Message): def parameters( self, ) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[ - ommx.v1.parameter_pb2.Parameter + global___Parameter ]: """Parameters of this instance @@ -74,8 +176,7 @@ class ParametricInstance(google.protobuf.message.Message): ommx.v1.decision_variables_pb2.DecisionVariable ] | None = ..., - parameters: collections.abc.Iterable[ommx.v1.parameter_pb2.Parameter] - | None = ..., + parameters: collections.abc.Iterable[global___Parameter] | None = ..., objective: ommx.v1.function_pb2.Function | None = ..., constraints: collections.abc.Iterable[ommx.v1.constraint_pb2.Constraint] | None = ..., From c3618299933ea83c10f8fa969438de1b710bfd75 Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Tue, 19 Nov 2024 21:21:26 +0900 Subject: [PATCH 07/29] Add parameters to the Instance message --- rust/ommx/src/mps/convert.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/rust/ommx/src/mps/convert.rs b/rust/ommx/src/mps/convert.rs index b03aef3c..09ad810d 100644 --- a/rust/ommx/src/mps/convert.rs +++ b/rust/ommx/src/mps/convert.rs @@ -16,6 +16,7 @@ pub fn convert(mps: Mps) -> Result { objective: Some(objective), constraints, sense: convert_sense(mps.obj_sense), + parameters: None, }) } From 411ed7c2cd30363acd8fc07c3ce01177e2dcded5 Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Tue, 19 Nov 2024 21:27:29 +0900 Subject: [PATCH 08/29] Remove outdated --- python/ommx/ommx/v1/parameter_pb2.py | 34 -------- python/ommx/ommx/v1/parameter_pb2.pyi | 116 -------------------------- 2 files changed, 150 deletions(-) delete mode 100644 python/ommx/ommx/v1/parameter_pb2.py delete mode 100644 python/ommx/ommx/v1/parameter_pb2.pyi diff --git a/python/ommx/ommx/v1/parameter_pb2.py b/python/ommx/ommx/v1/parameter_pb2.py deleted file mode 100644 index f485606a..00000000 --- a/python/ommx/ommx/v1/parameter_pb2.py +++ /dev/null @@ -1,34 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by the protocol buffer compiler. DO NOT EDIT! -# source: ommx/v1/parameter.proto -# Protobuf Python Version: 5.26.1 -"""Generated protocol buffer code.""" - -from google.protobuf import descriptor as _descriptor -from google.protobuf import descriptor_pool as _descriptor_pool -from google.protobuf import symbol_database as _symbol_database -from google.protobuf.internal import builder as _builder -# @@protoc_insertion_point(imports) - -_sym_db = _symbol_database.Default() - - -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile( - b'\n\x17ommx/v1/parameter.proto\x12\x07ommx.v1"\x97\x02\n\tParameter\x12\x0e\n\x02id\x18\x01 \x01(\x04R\x02id\x12\x17\n\x04name\x18\x02 \x01(\tH\x00R\x04name\x88\x01\x01\x12\x1e\n\nsubscripts\x18\x03 \x03(\x03R\nsubscripts\x12\x42\n\nparameters\x18\x04 \x03(\x0b\x32".ommx.v1.Parameter.ParametersEntryR\nparameters\x12%\n\x0b\x64\x65scription\x18\x05 \x01(\tH\x01R\x0b\x64\x65scription\x88\x01\x01\x1a=\n\x0fParametersEntry\x12\x10\n\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n\x05value\x18\x02 \x01(\tR\x05value:\x02\x38\x01\x42\x07\n\x05_nameB\x0e\n\x0c_descriptionBZ\n\x0b\x63om.ommx.v1B\x0eParameterProtoP\x01\xa2\x02\x03OXX\xaa\x02\x07Ommx.V1\xca\x02\x07Ommx\\V1\xe2\x02\x13Ommx\\V1\\GPBMetadata\xea\x02\x08Ommx::V1b\x06proto3' -) - -_globals = globals() -_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) -_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, "ommx.v1.parameter_pb2", _globals) -if not _descriptor._USE_C_DESCRIPTORS: - _globals["DESCRIPTOR"]._loaded_options = None - _globals[ - "DESCRIPTOR" - ]._serialized_options = b"\n\013com.ommx.v1B\016ParameterProtoP\001\242\002\003OXX\252\002\007Ommx.V1\312\002\007Ommx\\V1\342\002\023Ommx\\V1\\GPBMetadata\352\002\010Ommx::V1" - _globals["_PARAMETER_PARAMETERSENTRY"]._loaded_options = None - _globals["_PARAMETER_PARAMETERSENTRY"]._serialized_options = b"8\001" - _globals["_PARAMETER"]._serialized_start = 37 - _globals["_PARAMETER"]._serialized_end = 316 - _globals["_PARAMETER_PARAMETERSENTRY"]._serialized_start = 230 - _globals["_PARAMETER_PARAMETERSENTRY"]._serialized_end = 291 -# @@protoc_insertion_point(module_scope) diff --git a/python/ommx/ommx/v1/parameter_pb2.pyi b/python/ommx/ommx/v1/parameter_pb2.pyi deleted file mode 100644 index 04e27310..00000000 --- a/python/ommx/ommx/v1/parameter_pb2.pyi +++ /dev/null @@ -1,116 +0,0 @@ -""" -@generated by mypy-protobuf. Do not edit manually! -isort:skip_file -""" - -import builtins -import collections.abc -import google.protobuf.descriptor -import google.protobuf.internal.containers -import google.protobuf.message -import typing - -DESCRIPTOR: google.protobuf.descriptor.FileDescriptor - -@typing.final -class Parameter(google.protobuf.message.Message): - """Placeholder of a parameter in a parametrized optimization problem""" - - DESCRIPTOR: google.protobuf.descriptor.Descriptor - - @typing.final - class ParametersEntry(google.protobuf.message.Message): - DESCRIPTOR: google.protobuf.descriptor.Descriptor - - KEY_FIELD_NUMBER: builtins.int - VALUE_FIELD_NUMBER: builtins.int - key: builtins.str - value: builtins.str - def __init__( - self, - *, - key: builtins.str = ..., - value: builtins.str = ..., - ) -> None: ... - def ClearField( - self, field_name: typing.Literal["key", b"key", "value", b"value"] - ) -> None: ... - - ID_FIELD_NUMBER: builtins.int - NAME_FIELD_NUMBER: builtins.int - SUBSCRIPTS_FIELD_NUMBER: builtins.int - PARAMETERS_FIELD_NUMBER: builtins.int - DESCRIPTION_FIELD_NUMBER: builtins.int - id: builtins.int - """ID for the parameter - - - IDs are not required to be sequential. - - The ID must be unique within the instance including the decision variables. - """ - name: builtins.str - """Name of the parameter. e.g. `x`""" - description: builtins.str - """Human-readable description for the parameter""" - @property - def subscripts( - self, - ) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.int]: - """Subscripts of the parameter, same usage as DecisionVariable.subscripts""" - - @property - def parameters( - self, - ) -> google.protobuf.internal.containers.ScalarMap[builtins.str, builtins.str]: - """Additional metadata for the parameter, same usage as DecisionVariable.parameters""" - - def __init__( - self, - *, - id: builtins.int = ..., - name: builtins.str | None = ..., - subscripts: collections.abc.Iterable[builtins.int] | None = ..., - parameters: collections.abc.Mapping[builtins.str, builtins.str] | None = ..., - description: builtins.str | None = ..., - ) -> None: ... - def HasField( - self, - field_name: typing.Literal[ - "_description", - b"_description", - "_name", - b"_name", - "description", - b"description", - "name", - b"name", - ], - ) -> builtins.bool: ... - def ClearField( - self, - field_name: typing.Literal[ - "_description", - b"_description", - "_name", - b"_name", - "description", - b"description", - "id", - b"id", - "name", - b"name", - "parameters", - b"parameters", - "subscripts", - b"subscripts", - ], - ) -> None: ... - @typing.overload - def WhichOneof( - self, oneof_group: typing.Literal["_description", b"_description"] - ) -> typing.Literal["description"] | None: ... - @typing.overload - def WhichOneof( - self, oneof_group: typing.Literal["_name", b"_name"] - ) -> typing.Literal["name"] | None: ... - -global___Parameter = Parameter From 4cc29ab5d268dedd9d6b5660c55a66e87245d192 Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Tue, 19 Nov 2024 21:45:49 +0900 Subject: [PATCH 09/29] Moc for ParametricInstance::create_instance --- rust/ommx/src/convert.rs | 1 + rust/ommx/src/convert/parametric_instance.rs | 8 ++++++++ 2 files changed, 9 insertions(+) create mode 100644 rust/ommx/src/convert/parametric_instance.rs diff --git a/rust/ommx/src/convert.rs b/rust/ommx/src/convert.rs index 66ca3c7e..a7babef4 100644 --- a/rust/ommx/src/convert.rs +++ b/rust/ommx/src/convert.rs @@ -144,6 +144,7 @@ macro_rules! test_algebraic { mod format; mod function; mod linear; +mod parametric_instance; mod polynomial; mod quadratic; diff --git a/rust/ommx/src/convert/parametric_instance.rs b/rust/ommx/src/convert/parametric_instance.rs new file mode 100644 index 00000000..e24b15df --- /dev/null +++ b/rust/ommx/src/convert/parametric_instance.rs @@ -0,0 +1,8 @@ +use crate::v1::{Instance, Parameters, ParametricInstance}; +use anyhow::Result; + +impl ParametricInstance { + pub fn create_instance(&self, parameters: Parameters) -> Result { + todo!() + } +} From e093d1b3ad6587d9cc8c5b7551b7940cc8627507 Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Wed, 20 Nov 2024 12:21:22 +0900 Subject: [PATCH 10/29] Add doc_rust task --- Taskfile.yml | 9 +++++++++ rust/ommx/src/convert/parametric_instance.rs | 3 ++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/Taskfile.yml b/Taskfile.yml index eec352b3..c1e35bcf 100644 --- a/Taskfile.yml +++ b/Taskfile.yml @@ -3,6 +3,15 @@ version: "3" tasks: + # Documents + doc_rust: + cmds: + - cargo doc --no-deps -p ommx + + doc_rust_open: + cmds: + - cargo doc --no-deps --open -p ommx + # Protocol Buffers protogen: cmds: diff --git a/rust/ommx/src/convert/parametric_instance.rs b/rust/ommx/src/convert/parametric_instance.rs index e24b15df..e3cb1773 100644 --- a/rust/ommx/src/convert/parametric_instance.rs +++ b/rust/ommx/src/convert/parametric_instance.rs @@ -2,7 +2,8 @@ use crate::v1::{Instance, Parameters, ParametricInstance}; use anyhow::Result; impl ParametricInstance { - pub fn create_instance(&self, parameters: Parameters) -> Result { + /// Create a new [Instance] with the given parameters. + pub fn with_parameters(&self, parameters: Parameters) -> Result { todo!() } } From 35551606a97001c566dcfabe3cec5635951b0228 Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Wed, 20 Nov 2024 12:43:33 +0900 Subject: [PATCH 11/29] impl From for ParametricInstance --- rust/ommx/src/convert/parametric_instance.rs | 22 ++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/rust/ommx/src/convert/parametric_instance.rs b/rust/ommx/src/convert/parametric_instance.rs index e3cb1773..942a1e3a 100644 --- a/rust/ommx/src/convert/parametric_instance.rs +++ b/rust/ommx/src/convert/parametric_instance.rs @@ -1,6 +1,28 @@ use crate::v1::{Instance, Parameters, ParametricInstance}; use anyhow::Result; +impl From for ParametricInstance { + fn from( + Instance { + description, + objective, + constraints, + decision_variables, + sense, + parameters: _, // Drop previous parameters + }: Instance, + ) -> Self { + Self { + description, + objective, + constraints, + decision_variables, + sense, + parameters: Default::default(), + } + } +} + impl ParametricInstance { /// Create a new [Instance] with the given parameters. pub fn with_parameters(&self, parameters: Parameters) -> Result { From bdbfbe6f1786308cfa0a276ef68e91d32115bacb Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Wed, 20 Nov 2024 17:54:28 +0900 Subject: [PATCH 12/29] Partial evaluate --- rust/ommx/src/convert/parametric_instance.rs | 16 ++++- rust/ommx/src/evaluate.rs | 65 ++++++++++++++++++++ 2 files changed, 79 insertions(+), 2 deletions(-) diff --git a/rust/ommx/src/convert/parametric_instance.rs b/rust/ommx/src/convert/parametric_instance.rs index 942a1e3a..9189a6b2 100644 --- a/rust/ommx/src/convert/parametric_instance.rs +++ b/rust/ommx/src/convert/parametric_instance.rs @@ -1,4 +1,4 @@ -use crate::v1::{Instance, Parameters, ParametricInstance}; +use crate::v1::{Instance, Parameters, ParametricInstance, State}; use anyhow::Result; impl From for ParametricInstance { @@ -23,9 +23,21 @@ impl From for ParametricInstance { } } +impl From for Parameters { + fn from(State { entries }: State) -> Self { + Self { entries } + } +} + +impl From for State { + fn from(Parameters { entries }: Parameters) -> Self { + Self { entries } + } +} + impl ParametricInstance { /// Create a new [Instance] with the given parameters. - pub fn with_parameters(&self, parameters: Parameters) -> Result { + pub fn with_parameters(&self, _parameters: Parameters) -> Result { todo!() } } diff --git a/rust/ommx/src/evaluate.rs b/rust/ommx/src/evaluate.rs index 3e86a995..df179bd0 100644 --- a/rust/ommx/src/evaluate.rs +++ b/rust/ommx/src/evaluate.rs @@ -11,6 +11,9 @@ pub trait Evaluate { type Output; /// Evaluate to return the output with used variable ids fn evaluate(&self, solution: &State) -> Result<(Self::Output, BTreeSet)>; + + /// Partially evaluate the function to return the used variable ids + fn partial_evaluate(&mut self, state: &State) -> Result>; } impl Evaluate for Function { @@ -25,6 +28,16 @@ impl Evaluate for Function { }; Ok(out) } + + fn partial_evaluate(&mut self, state: &State) -> Result> { + Ok(match &mut self.function { + Some(FunctionEnum::Constant(_)) => BTreeSet::new(), + Some(FunctionEnum::Linear(linear)) => linear.partial_evaluate(state)?, + Some(FunctionEnum::Quadratic(quadratic)) => quadratic.partial_evaluate(state)?, + Some(FunctionEnum::Polynomial(poly)) => poly.partial_evaluate(state)?, + None => bail!("Function is not set"), + }) + } } impl Evaluate for Linear { @@ -42,6 +55,22 @@ impl Evaluate for Linear { } Ok((sum, used_ids)) } + + fn partial_evaluate(&mut self, state: &State) -> Result> { + let mut used = BTreeSet::new(); + let mut i = 0; + while i < self.terms.len() { + let LinearTerm { id, coefficient } = self.terms[i]; + if let Some(value) = state.entries.get(&id) { + self.constant += coefficient * value; + self.terms.swap_remove(i); + used.insert(id); + } else { + i += 1; + } + } + Ok(used) + } } impl Evaluate for Quadratic { @@ -70,6 +99,10 @@ impl Evaluate for Quadratic { } Ok((sum, used_ids)) } + + fn partial_evaluate(&mut self, _state: &State) -> Result> { + todo!() + } } impl Evaluate for Polynomial { @@ -90,6 +123,10 @@ impl Evaluate for Polynomial { } Ok((sum, used_ids)) } + + fn partial_evaluate(&mut self, _state: &State) -> Result> { + todo!() + } } impl Evaluate for Constraint { @@ -117,6 +154,10 @@ impl Evaluate for Constraint { used_ids, )) } + + fn partial_evaluate(&mut self, _state: &State) -> Result> { + todo!() + } } impl Evaluate for Instance { @@ -163,4 +204,28 @@ impl Evaluate for Instance { used_ids, )) } + + fn partial_evaluate(&mut self, _state: &State) -> Result> { + todo!() + } +} + +#[cfg(test)] +mod tests { + use super::*; + use maplit::*; + + #[test] + fn linear_partial_evaluate() { + let mut linear = Linear::new([(1, 1.0), (2, 2.0), (3, 3.0), (4, 4.0)].into_iter(), 5.0); + let state = State { + entries: hashmap! { 1 => 1.0, 2 => 2.0, 3 => 3.0, 5 => 5.0, 6 => 6.0 }, + }; + let used = linear.partial_evaluate(&state).unwrap(); + assert_eq!(used, btreeset! { 1, 2, 3 }); + assert_eq!(linear.constant, 5.0 + 1.0 * 1.0 + 2.0 * 2.0 + 3.0 * 3.0); + assert_eq!(linear.terms.len(), 1); + assert_eq!(linear.terms[0].id, 4); + assert_eq!(linear.terms[0].coefficient, 4.0); + } } From d8e8cde07f2a3f0d0a063f18b482a5040ed2c7d5 Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Wed, 20 Nov 2024 20:30:05 +0900 Subject: [PATCH 13/29] WIP: partial_evaluate for Quadratic (without test) --- rust/ommx/src/evaluate.rs | 51 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 47 insertions(+), 4 deletions(-) diff --git a/rust/ommx/src/evaluate.rs b/rust/ommx/src/evaluate.rs index df179bd0..e02f064a 100644 --- a/rust/ommx/src/evaluate.rs +++ b/rust/ommx/src/evaluate.rs @@ -3,8 +3,8 @@ use crate::v1::{ EvaluatedConstraint, Function, Instance, Linear, Optimality, Polynomial, Quadratic, Relaxation, Solution, State, }; -use anyhow::{bail, Context, Result}; -use std::collections::BTreeSet; +use anyhow::{bail, ensure, Context, Result}; +use std::collections::{BTreeMap, BTreeSet}; /// Evaluate with a [State] pub trait Evaluate { @@ -100,8 +100,51 @@ impl Evaluate for Quadratic { Ok((sum, used_ids)) } - fn partial_evaluate(&mut self, _state: &State) -> Result> { - todo!() + fn partial_evaluate(&mut self, state: &State) -> Result> { + let mut used = BTreeSet::new(); + let mut linear = BTreeMap::new(); + let mut constant = self.linear.as_ref().map_or(0.0, |l| l.constant); + for term in self.linear.iter().map(|l| l.terms.iter()).flatten() { + if let Some(value) = state.entries.get(&term.id) { + constant += term.coefficient * value; + used.insert(term.id); + } else { + *linear.entry(term.id).or_insert(0.0) += term.coefficient; + } + } + + ensure!(self.rows.len() == self.columns.len()); + ensure!(self.rows.len() == self.values.len()); + let mut i = 0; + while i < self.rows.len() { + let (row, column, value) = (self.rows[i], self.columns[i], self.values[i]); + match (state.entries.get(&row), state.entries.get(&column)) { + (Some(u), Some(v)) => { + constant += value * u * v; + used.insert(row); + used.insert(column); + } + (Some(u), None) => { + *linear.entry(column).or_insert(0.0) += value * u; + used.insert(row); + } + (None, Some(v)) => { + *linear.entry(row).or_insert(0.0) += value * v; + used.insert(column); + } + _ => { + i += 1; + continue; + } + } + self.rows.swap_remove(i); + self.columns.swap_remove(i); + self.values.swap_remove(i); + } + if !linear.is_empty() || constant != 0.0 { + self.linear = Some(Linear::new(linear.into_iter(), constant)); + } + Ok(used) } } From 02f9faff8b716e929afa3c6985124e5b125aaf6a Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Wed, 20 Nov 2024 20:46:46 +0900 Subject: [PATCH 14/29] Add convert::state submod --- rust/ommx/src/convert.rs | 10 +--------- rust/ommx/src/convert/state.rs | 25 +++++++++++++++++++++++++ 2 files changed, 26 insertions(+), 9 deletions(-) create mode 100644 rust/ommx/src/convert/state.rs diff --git a/rust/ommx/src/convert.rs b/rust/ommx/src/convert.rs index a7babef4..8d55a559 100644 --- a/rust/ommx/src/convert.rs +++ b/rust/ommx/src/convert.rs @@ -147,12 +147,4 @@ mod linear; mod parametric_instance; mod polynomial; mod quadratic; - -use crate::v1::State; -use std::collections::HashMap; - -impl From> for State { - fn from(entries: HashMap) -> Self { - Self { entries } - } -} +mod state; diff --git a/rust/ommx/src/convert/state.rs b/rust/ommx/src/convert/state.rs new file mode 100644 index 00000000..41a05ad7 --- /dev/null +++ b/rust/ommx/src/convert/state.rs @@ -0,0 +1,25 @@ +use crate::v1::State; +use std::collections::HashMap; + +impl From> for State { + fn from(value: HashMap) -> Self { + Self { entries: value } + } +} + +impl FromIterator<(u64, f64)> for State { + fn from_iter>(iter: T) -> Self { + Self { + entries: iter.into_iter().collect(), + } + } +} + +impl IntoIterator for State { + type Item = (u64, f64); + type IntoIter = std::collections::hash_map::IntoIter; + + fn into_iter(self) -> Self::IntoIter { + self.entries.into_iter() + } +} From 2ef3cfa6f901df9c7fd251c160c445b859f907dc Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Wed, 20 Nov 2024 20:53:07 +0900 Subject: [PATCH 15/29] Use parametric Arbitrary for Function, Linear, Polynomial, and Quadratic --- rust/ommx/src/convert/function.rs | 16 +++++++++++----- rust/ommx/src/convert/linear.rs | 19 ++++++++++--------- rust/ommx/src/convert/polynomial.rs | 27 +++++++++++++++------------ rust/ommx/src/convert/quadratic.rs | 26 +++++++++++++------------- 4 files changed, 49 insertions(+), 39 deletions(-) diff --git a/rust/ommx/src/convert/function.rs b/rust/ommx/src/convert/function.rs index 0cd3a89d..908c6d51 100644 --- a/rust/ommx/src/convert/function.rs +++ b/rust/ommx/src/convert/function.rs @@ -217,18 +217,24 @@ impl Product for Function { } impl Arbitrary for Function { - type Parameters = (); + type Parameters = (usize, usize, u64); type Strategy = BoxedStrategy; - fn arbitrary_with(_: Self::Parameters) -> Self::Strategy { + fn arbitrary_with((num_terms, max_degree, max_id): Self::Parameters) -> Self::Strategy { prop_oneof![ prop_oneof![Just(0.0), -1.0..1.0_f64].prop_map(Function::from), - any::().prop_map(Function::from), - any::().prop_map(Function::from), - any::().prop_map(Function::from), + Linear::arbitrary_with((num_terms, max_id)).prop_map(Function::from), + Quadratic::arbitrary_with((num_terms, max_id)).prop_map(Function::from), + Polynomial::arbitrary_with((num_terms, max_degree, max_id)).prop_map(Function::from), ] .boxed() } + + fn arbitrary() -> Self::Strategy { + (0..10_usize, 0..5_usize, 0..10_u64) + .prop_flat_map(Self::arbitrary_with) + .boxed() + } } impl AbsDiffEq for Function { diff --git a/rust/ommx/src/convert/linear.rs b/rust/ommx/src/convert/linear.rs index c4e93bd8..22ee8ce7 100644 --- a/rust/ommx/src/convert/linear.rs +++ b/rust/ommx/src/convert/linear.rs @@ -198,22 +198,23 @@ impl Mul for Linear { } impl Arbitrary for Linear { - type Parameters = (); + type Parameters = (usize /* num_terms */, u64 /* Max ID */); type Strategy = BoxedStrategy; - fn arbitrary_with(_: Self::Parameters) -> Self::Strategy { - let num_terms = 0..10_usize; - let terms = num_terms.prop_flat_map(|num_terms| { - proptest::collection::vec( - (0..(2 * num_terms as u64), prop_oneof![Just(0.0), -1.0..1.0]), - num_terms, - ) - }); + fn arbitrary_with((num_terms, max_id): Self::Parameters) -> Self::Strategy { + let terms = + proptest::collection::vec((0..=max_id, prop_oneof![Just(0.0), -1.0..1.0]), num_terms); let constant = prop_oneof![Just(0.0), -1.0..1.0]; (terms, constant) .prop_map(|(terms, constant)| Linear::new(terms.into_iter(), constant)) .boxed() } + + fn arbitrary() -> Self::Strategy { + (0..5_usize, 0..10_u64) + .prop_flat_map(Self::arbitrary_with) + .boxed() + } } /// Compare coefficients in sup-norm. diff --git a/rust/ommx/src/convert/polynomial.rs b/rust/ommx/src/convert/polynomial.rs index 01029794..26358549 100644 --- a/rust/ommx/src/convert/polynomial.rs +++ b/rust/ommx/src/convert/polynomial.rs @@ -160,22 +160,25 @@ impl_mul_inverse!(Quadratic, Polynomial); impl_neg_by_mul!(Polynomial); impl Arbitrary for Polynomial { - type Parameters = (); + type Parameters = (usize, usize, u64); type Strategy = BoxedStrategy; - fn arbitrary_with(_: Self::Parameters) -> Self::Strategy { - let num_terms = 0..10_usize; - let terms = num_terms.prop_flat_map(|num_terms| { - proptest::collection::vec( - ( - proptest::collection::vec(0..(2 * num_terms as u64), 0..=num_terms), - prop_oneof![Just(0.0), -1.0..1.0], - ), - num_terms, - ) - }); + fn arbitrary_with((num_terms, max_degree, max_id): Self::Parameters) -> Self::Strategy { + let terms = proptest::collection::vec( + ( + proptest::collection::vec(0..=max_id, 0..=max_degree), + prop_oneof![Just(0.0), -1.0..1.0], + ), + num_terms, + ); terms.prop_map(|terms| terms.into_iter().collect()).boxed() } + + fn arbitrary() -> Self::Strategy { + (0..10_usize, 0..5_usize, 0..10_u64) + .prop_flat_map(Self::arbitrary_with) + .boxed() + } } /// Compare coefficients in sup-norm. diff --git a/rust/ommx/src/convert/quadratic.rs b/rust/ommx/src/convert/quadratic.rs index 3a65ea87..01a87a73 100644 --- a/rust/ommx/src/convert/quadratic.rs +++ b/rust/ommx/src/convert/quadratic.rs @@ -218,21 +218,15 @@ impl_mul_inverse!(f64, Quadratic); impl_neg_by_mul!(Quadratic); impl Arbitrary for Quadratic { - type Parameters = (); + type Parameters = (usize /* num_terms */, u64 /* max id */); type Strategy = BoxedStrategy; - fn arbitrary_with(_: Self::Parameters) -> Self::Strategy { - let num_terms = 0..10_usize; - let terms = num_terms.prop_flat_map(|num_terms| { - proptest::collection::vec( - ( - (0..(2 * num_terms as u64), 0..(2 * num_terms as u64)), - prop_oneof![Just(0.0), -1.0..1.0], - ), - num_terms, - ) - }); - let linear = Linear::arbitrary_with(()); + fn arbitrary_with((num_terms, max_id): Self::Parameters) -> Self::Strategy { + let terms = proptest::collection::vec( + ((0..=max_id, 0..=max_id), prop_oneof![Just(0.0), -1.0..1.0]), + num_terms, + ); + let linear = Linear::arbitrary_with((num_terms, max_id)); (terms, linear) .prop_map(|(terms, linear)| { let mut quad: Quadratic = terms.into_iter().collect(); @@ -241,6 +235,12 @@ impl Arbitrary for Quadratic { }) .boxed() } + + fn arbitrary() -> Self::Strategy { + (0..5_usize, 0..10_u64) + .prop_flat_map(Self::arbitrary_with) + .boxed() + } } /// Compare coefficients in sup-norm. From cd542d88344c36d48eddd670bf921ef00cbff206 Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Wed, 20 Nov 2024 20:53:31 +0900 Subject: [PATCH 16/29] clippy fix --- rust/ommx/src/evaluate.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust/ommx/src/evaluate.rs b/rust/ommx/src/evaluate.rs index e02f064a..87e3d7bf 100644 --- a/rust/ommx/src/evaluate.rs +++ b/rust/ommx/src/evaluate.rs @@ -104,7 +104,7 @@ impl Evaluate for Quadratic { let mut used = BTreeSet::new(); let mut linear = BTreeMap::new(); let mut constant = self.linear.as_ref().map_or(0.0, |l| l.constant); - for term in self.linear.iter().map(|l| l.terms.iter()).flatten() { + for term in self.linear.iter().flat_map(|l| l.terms.iter()) { if let Some(value) = state.entries.get(&term.id) { constant += term.coefficient * value; used.insert(term.id); From 12ed71ec3e87b13b7f636bcc4b485565e3839f09 Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Wed, 20 Nov 2024 21:20:17 +0900 Subject: [PATCH 17/29] arbitrary_coefficient --- rust/ommx/src/convert.rs | 6 ++++++ rust/ommx/src/convert/function.rs | 2 +- rust/ommx/src/convert/linear.rs | 7 ++++--- rust/ommx/src/convert/polynomial.rs | 4 ++-- rust/ommx/src/convert/quadratic.rs | 4 ++-- rust/ommx/src/convert/state.rs | 18 ++++++++++++++++++ rust/ommx/src/evaluate.rs | 20 ++++++++++++++++++++ 7 files changed, 53 insertions(+), 8 deletions(-) diff --git a/rust/ommx/src/convert.rs b/rust/ommx/src/convert.rs index 8d55a559..8680bb19 100644 --- a/rust/ommx/src/convert.rs +++ b/rust/ommx/src/convert.rs @@ -148,3 +148,9 @@ mod parametric_instance; mod polynomial; mod quadratic; mod state; + +use proptest::prelude::*; + +fn arbitrary_coefficient() -> BoxedStrategy { + prop_oneof![Just(0.0), Just(1.0), Just(-1.0), -1.0..1.0].boxed() +} diff --git a/rust/ommx/src/convert/function.rs b/rust/ommx/src/convert/function.rs index 908c6d51..f0c6dd69 100644 --- a/rust/ommx/src/convert/function.rs +++ b/rust/ommx/src/convert/function.rs @@ -222,7 +222,7 @@ impl Arbitrary for Function { fn arbitrary_with((num_terms, max_degree, max_id): Self::Parameters) -> Self::Strategy { prop_oneof![ - prop_oneof![Just(0.0), -1.0..1.0_f64].prop_map(Function::from), + super::arbitrary_coefficient().prop_map(Function::from), Linear::arbitrary_with((num_terms, max_id)).prop_map(Function::from), Quadratic::arbitrary_with((num_terms, max_id)).prop_map(Function::from), Polynomial::arbitrary_with((num_terms, max_degree, max_id)).prop_map(Function::from), diff --git a/rust/ommx/src/convert/linear.rs b/rust/ommx/src/convert/linear.rs index 22ee8ce7..118d82a7 100644 --- a/rust/ommx/src/convert/linear.rs +++ b/rust/ommx/src/convert/linear.rs @@ -9,6 +9,8 @@ use std::{ ops::*, }; +use super::arbitrary_coefficient; + impl Zero for Linear { fn zero() -> Self { Self::from(0.0) @@ -202,9 +204,8 @@ impl Arbitrary for Linear { type Strategy = BoxedStrategy; fn arbitrary_with((num_terms, max_id): Self::Parameters) -> Self::Strategy { - let terms = - proptest::collection::vec((0..=max_id, prop_oneof![Just(0.0), -1.0..1.0]), num_terms); - let constant = prop_oneof![Just(0.0), -1.0..1.0]; + let terms = proptest::collection::vec((0..=max_id, arbitrary_coefficient()), num_terms); + let constant = arbitrary_coefficient(); (terms, constant) .prop_map(|(terms, constant)| Linear::new(terms.into_iter(), constant)) .boxed() diff --git a/rust/ommx/src/convert/polynomial.rs b/rust/ommx/src/convert/polynomial.rs index 26358549..d3375f5f 100644 --- a/rust/ommx/src/convert/polynomial.rs +++ b/rust/ommx/src/convert/polynomial.rs @@ -8,7 +8,7 @@ use std::{ ops::{Add, Mul}, }; -use super::format::format_polynomial; +use super::{arbitrary_coefficient, format::format_polynomial}; impl Zero for Polynomial { fn zero() -> Self { @@ -167,7 +167,7 @@ impl Arbitrary for Polynomial { let terms = proptest::collection::vec( ( proptest::collection::vec(0..=max_id, 0..=max_degree), - prop_oneof![Just(0.0), -1.0..1.0], + arbitrary_coefficient(), ), num_terms, ); diff --git a/rust/ommx/src/convert/quadratic.rs b/rust/ommx/src/convert/quadratic.rs index 01a87a73..860a7ae2 100644 --- a/rust/ommx/src/convert/quadratic.rs +++ b/rust/ommx/src/convert/quadratic.rs @@ -8,7 +8,7 @@ use std::{ ops::{Add, Mul}, }; -use super::format::format_polynomial; +use super::{arbitrary_coefficient, format::format_polynomial}; impl Zero for Quadratic { fn zero() -> Self { @@ -223,7 +223,7 @@ impl Arbitrary for Quadratic { fn arbitrary_with((num_terms, max_id): Self::Parameters) -> Self::Strategy { let terms = proptest::collection::vec( - ((0..=max_id, 0..=max_id), prop_oneof![Just(0.0), -1.0..1.0]), + ((0..=max_id, 0..=max_id), arbitrary_coefficient()), num_terms, ); let linear = Linear::arbitrary_with((num_terms, max_id)); diff --git a/rust/ommx/src/convert/state.rs b/rust/ommx/src/convert/state.rs index 41a05ad7..5134d6a0 100644 --- a/rust/ommx/src/convert/state.rs +++ b/rust/ommx/src/convert/state.rs @@ -1,4 +1,5 @@ use crate::v1::State; +use proptest::prelude::*; use std::collections::HashMap; impl From> for State { @@ -23,3 +24,20 @@ impl IntoIterator for State { self.entries.into_iter() } } + +impl Arbitrary for State { + type Parameters = (usize, u64); + type Strategy = BoxedStrategy; + + fn arbitrary_with((size, max_id): Self::Parameters) -> Self::Strategy { + proptest::collection::hash_map(0..=max_id, super::arbitrary_coefficient(), 0..=size) + .prop_map(Self::from) + .boxed() + } + + fn arbitrary() -> Self::Strategy { + (0..20_usize, 0..20_u64) + .prop_flat_map(Self::arbitrary_with) + .boxed() + } +} diff --git a/rust/ommx/src/evaluate.rs b/rust/ommx/src/evaluate.rs index 87e3d7bf..970c2d8d 100644 --- a/rust/ommx/src/evaluate.rs +++ b/rust/ommx/src/evaluate.rs @@ -256,7 +256,9 @@ impl Evaluate for Instance { #[cfg(test)] mod tests { use super::*; + use approx::*; use maplit::*; + use proptest::prelude::*; #[test] fn linear_partial_evaluate() { @@ -271,4 +273,22 @@ mod tests { assert_eq!(linear.terms[0].id, 4); assert_eq!(linear.terms[0].coefficient, 4.0); } + + proptest! { + #[test] + fn linear_evaluate_add(f in any::(), g in any::(), s in any::()) { + let (Ok((f_value, f_used)), Ok((g_value, g_used))) = (f.evaluate(&s), g.evaluate(&s)) else { return Ok(()); }; + let (h_value, h_used) = (f + g).evaluate(&s).unwrap(); + prop_assert!(abs_diff_eq!(f_value + g_value, h_value)); + prop_assert_eq!(f_used.union(&g_used).cloned().collect::>(), h_used); + } + + #[test] + fn linear_evaluate_mul(f in any::(), g in any::(), s in any::()) { + let (Ok((f_value, f_used)), Ok((g_value, g_used))) = (f.evaluate(&s), g.evaluate(&s)) else { return Ok(()); }; + let (h_value, h_used) = (f * g).evaluate(&s).unwrap(); + prop_assert!(abs_diff_eq!(f_value * g_value, h_value)); + prop_assert_eq!(f_used.union(&g_used).cloned().collect::>(), h_used); + } + } } From 458593628676cb6fb33b1413e13cac3d0173b836 Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Wed, 20 Nov 2024 21:26:57 +0900 Subject: [PATCH 18/29] Add zero check in Polynomial addition --- rust/ommx/src/convert/polynomial.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/rust/ommx/src/convert/polynomial.rs b/rust/ommx/src/convert/polynomial.rs index d3375f5f..d10ed7ca 100644 --- a/rust/ommx/src/convert/polynomial.rs +++ b/rust/ommx/src/convert/polynomial.rs @@ -104,7 +104,11 @@ impl Add for Polynomial { fn add(self, rhs: Self) -> Self { let mut terms = BTreeMap::new(); for term in self.terms.iter().chain(rhs.terms.iter()) { - *terms.entry(term.ids.clone()).or_default() += term.coefficient; + let value: &mut f64 = terms.entry(term.ids.clone()).or_default(); + *value += term.coefficient; + if value.abs() <= f64::EPSILON { + terms.remove(&term.ids); + } } Self { terms: terms From 7a59be96aaa859d50473c3457ee1bcfa04402f43 Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Wed, 20 Nov 2024 21:34:15 +0900 Subject: [PATCH 19/29] Do not compare used variables in tests --- rust/ommx/src/evaluate.rs | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/rust/ommx/src/evaluate.rs b/rust/ommx/src/evaluate.rs index 970c2d8d..142cdb4c 100644 --- a/rust/ommx/src/evaluate.rs +++ b/rust/ommx/src/evaluate.rs @@ -277,18 +277,16 @@ mod tests { proptest! { #[test] fn linear_evaluate_add(f in any::(), g in any::(), s in any::()) { - let (Ok((f_value, f_used)), Ok((g_value, g_used))) = (f.evaluate(&s), g.evaluate(&s)) else { return Ok(()); }; - let (h_value, h_used) = (f + g).evaluate(&s).unwrap(); - prop_assert!(abs_diff_eq!(f_value + g_value, h_value)); - prop_assert_eq!(f_used.union(&g_used).cloned().collect::>(), h_used); + let (Ok((f_value, _)), Ok((g_value, _))) = (f.evaluate(&s), g.evaluate(&s)) else { return Ok(()); }; + let (h_value, _) = (f + g).evaluate(&s).unwrap(); + prop_assert!(abs_diff_eq!(dbg!(f_value + g_value), dbg!(h_value), epsilon = 1e-9)); } #[test] fn linear_evaluate_mul(f in any::(), g in any::(), s in any::()) { - let (Ok((f_value, f_used)), Ok((g_value, g_used))) = (f.evaluate(&s), g.evaluate(&s)) else { return Ok(()); }; - let (h_value, h_used) = (f * g).evaluate(&s).unwrap(); - prop_assert!(abs_diff_eq!(f_value * g_value, h_value)); - prop_assert_eq!(f_used.union(&g_used).cloned().collect::>(), h_used); + let (Ok((f_value, _)), Ok((g_value, _))) = (f.evaluate(&s), g.evaluate(&s)) else { return Ok(()); }; + let (h_value, _) = (f * g).evaluate(&s).unwrap(); + prop_assert!(abs_diff_eq!(dbg!(f_value * g_value), dbg!(h_value), epsilon = 1e-9)); } } } From d34cab862ac88e6726bae58be5330eeda69d301f Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Wed, 20 Nov 2024 21:43:04 +0900 Subject: [PATCH 20/29] More tests for evaluate --- rust/ommx/src/evaluate.rs | 42 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/rust/ommx/src/evaluate.rs b/rust/ommx/src/evaluate.rs index 142cdb4c..d5fa3fdc 100644 --- a/rust/ommx/src/evaluate.rs +++ b/rust/ommx/src/evaluate.rs @@ -288,5 +288,47 @@ mod tests { let (h_value, _) = (f * g).evaluate(&s).unwrap(); prop_assert!(abs_diff_eq!(dbg!(f_value * g_value), dbg!(h_value), epsilon = 1e-9)); } + + #[test] + fn quadratic_evaluate_add(f in any::(), g in any::(), s in any::()) { + let (Ok((f_value, _)), Ok((g_value, _))) = (f.evaluate(&s), g.evaluate(&s)) else { return Ok(()); }; + let (h_value, _) = (f + g).evaluate(&s).unwrap(); + prop_assert!(abs_diff_eq!(dbg!(f_value + g_value), dbg!(h_value), epsilon = 1e-9)); + } + + #[test] + fn quadratic_evaluate_mull(f in any::(), g in any::(), s in any::()) { + let (Ok((f_value, _)), Ok((g_value, _))) = (f.evaluate(&s), g.evaluate(&s)) else { return Ok(()); }; + let (h_value, _) = (f * g).evaluate(&s).unwrap(); + prop_assert!(abs_diff_eq!(dbg!(f_value * g_value), dbg!(h_value), epsilon = 1e-9)); + } + + #[test] + fn polynomial_evaluate_add(f in any::(), g in any::(), s in any::()) { + let (Ok((f_value, _)), Ok((g_value, _))) = (f.evaluate(&s), g.evaluate(&s)) else { return Ok(()); }; + let (h_value, _) = (f + g).evaluate(&s).unwrap(); + prop_assert!(abs_diff_eq!(dbg!(f_value + g_value), dbg!(h_value), epsilon = 1e-9)); + } + + #[test] + fn polynomial_evaluate_mul(f in any::(), g in any::(), s in any::()) { + let (Ok((f_value, _)), Ok((g_value, _))) = (f.evaluate(&s), g.evaluate(&s)) else { return Ok(()); }; + let (h_value, _) = (f * g).evaluate(&s).unwrap(); + prop_assert!(abs_diff_eq!(dbg!(f_value * g_value), dbg!(h_value), epsilon = 1e-9)); + } + + #[test] + fn function_evaluate_add(f in any::(), g in any::(), s in any::()) { + let (Ok((f_value, _)), Ok((g_value, _))) = (f.evaluate(&s), g.evaluate(&s)) else { return Ok(()); }; + let (h_value, _) = (f + g).evaluate(&s).unwrap(); + prop_assert!(abs_diff_eq!(dbg!(f_value + g_value), dbg!(h_value), epsilon = 1e-9)); + } + + #[test] + fn function_evaluate_mul(f in any::(), g in any::(), s in any::()) { + let (Ok((f_value, _)), Ok((g_value, _))) = (f.evaluate(&s), g.evaluate(&s)) else { return Ok(()); }; + let (h_value, _) = (f * g).evaluate(&s).unwrap(); + prop_assert!(abs_diff_eq!(dbg!(f_value * g_value), dbg!(h_value), epsilon = 1e-9)); + } } } From fb8ad08ccaa5349ea599d50239868232317cd8f3 Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Thu, 21 Nov 2024 17:57:40 +0900 Subject: [PATCH 21/29] ParametricInstance::with_parameters --- rust/ommx/src/convert/parametric_instance.rs | 42 ++++++++++++++++++-- 1 file changed, 38 insertions(+), 4 deletions(-) diff --git a/rust/ommx/src/convert/parametric_instance.rs b/rust/ommx/src/convert/parametric_instance.rs index 9189a6b2..d89f4b27 100644 --- a/rust/ommx/src/convert/parametric_instance.rs +++ b/rust/ommx/src/convert/parametric_instance.rs @@ -1,5 +1,9 @@ -use crate::v1::{Instance, Parameters, ParametricInstance, State}; -use anyhow::Result; +use crate::{ + v1::{Instance, Parameters, ParametricInstance, State}, + Evaluate, +}; +use anyhow::{bail, Context, Result}; +use std::collections::BTreeSet; impl From for ParametricInstance { fn from( @@ -37,7 +41,37 @@ impl From for State { impl ParametricInstance { /// Create a new [Instance] with the given parameters. - pub fn with_parameters(&self, _parameters: Parameters) -> Result { - todo!() + pub fn with_parameters(mut self, parameters: Parameters) -> Result { + let required_ids: BTreeSet = self.parameters.iter().map(|p| p.id).collect(); + let given_ids: BTreeSet = parameters.entries.keys().cloned().collect(); + if !required_ids.is_subset(&given_ids) { + for ids in required_ids.difference(&given_ids) { + let parameter = self.parameters.iter().find(|p| p.id == *ids).unwrap(); + log::error!("Missing parameter: {:?}", parameter); + } + bail!( + "Missing parameters: Required IDs {:?}, got {:?}", + required_ids, + given_ids + ); + } + + let state = State::from(parameters.clone()); + self.objective + .as_mut() + .context("Objective function of ParametricInstance is empty")? + .partial_evaluate(&state)?; + for constraint in self.constraints.iter_mut() { + constraint.partial_evaluate(&state)?; + } + + Ok(Instance { + description: self.description, + objective: self.objective, + constraints: self.constraints, + decision_variables: self.decision_variables, + sense: self.sense, + parameters: Some(parameters), + }) } } From 108fd72f0c0c02f23ea7b5e6bd679a8885891e1d Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Fri, 22 Nov 2024 18:21:26 +0900 Subject: [PATCH 22/29] Fill parameters in Instance --- rust/ommx/src/convert/instance.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/rust/ommx/src/convert/instance.rs b/rust/ommx/src/convert/instance.rs index 47a59be0..f6435f69 100644 --- a/rust/ommx/src/convert/instance.rs +++ b/rust/ommx/src/convert/instance.rs @@ -116,6 +116,7 @@ impl Arbitrary for Instance { decision_variables, description, sense: sense as i32, + ..Default::default() } }, ) From 37332796a7f9463a942f9863a8c2f6434113068f Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Fri, 22 Nov 2024 20:02:59 +0900 Subject: [PATCH 23/29] Limit size of generated subscripts and parameters in Arbitrary impl of DecisionVariable --- rust/ommx/src/convert/decision_variable.rs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/rust/ommx/src/convert/decision_variable.rs b/rust/ommx/src/convert/decision_variable.rs index cc9c9ee5..4d033d31 100644 --- a/rust/ommx/src/convert/decision_variable.rs +++ b/rust/ommx/src/convert/decision_variable.rs @@ -54,13 +54,21 @@ impl Arbitrary for DecisionVariable { type Strategy = BoxedStrategy; fn arbitrary_with(max_id: Self::Parameters) -> Self::Strategy { + let subscripts = prop_oneof![ + Just(Vec::::new()), + proptest::collection::vec(-(max_id as i64)..=(max_id as i64), 1..=3), + ]; + let parameters = prop_oneof![ + Just(HashMap::::new()), + proptest::collection::hash_map(String::arbitrary(), String::arbitrary(), 1..=3), + ]; ( 0..=max_id, Option::::arbitrary(), Option::::arbitrary(), Kind::arbitrary(), - Vec::::arbitrary(), - HashMap::::arbitrary(), + subscripts, + parameters, Option::::arbitrary(), ) .prop_map( From ee4b30dcba8f6083594c5db2494756fbd6a0b9d5 Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Fri, 22 Nov 2024 20:03:28 +0900 Subject: [PATCH 24/29] Test for ParametricInstance --- rust/ommx/src/convert/parametric_instance.rs | 49 +++++++++++++++++++- 1 file changed, 48 insertions(+), 1 deletion(-) diff --git a/rust/ommx/src/convert/parametric_instance.rs b/rust/ommx/src/convert/parametric_instance.rs index d89f4b27..67058dd4 100644 --- a/rust/ommx/src/convert/parametric_instance.rs +++ b/rust/ommx/src/convert/parametric_instance.rs @@ -1,5 +1,5 @@ use crate::{ - v1::{Instance, Parameters, ParametricInstance, State}, + v1::{Function, Instance, Parameters, ParametricInstance, State}, Evaluate, }; use anyhow::{bail, Context, Result}; @@ -74,4 +74,51 @@ impl ParametricInstance { parameters: Some(parameters), }) } + + pub fn objective(&self) -> Result<&Function> { + self.objective + .as_ref() + .context("Objective function of ParametricInstance is empty") + } + + pub fn used_ids(&self) -> Result> { + let mut used_ids = self.objective()?.used_decision_variable_ids(); + for c in &self.constraints { + used_ids.extend(c.function()?.used_decision_variable_ids()); + } + Ok(used_ids) + } + + pub fn defined_ids(&self) -> BTreeSet { + self.decision_variables + .iter() + .map(|dv| dv.id) + .collect::>() + } + + pub fn parameter_ids(&self) -> BTreeSet { + self.parameters.iter().map(|p| p.id).collect() + } +} + +#[cfg(test)] +mod tests { + use crate::convert::instance::InstanceParameter; + + use super::*; + use proptest::prelude::*; + + proptest! { + #[test] + fn test_parametric_instance_conversion(instance in Instance::arbitrary_with(InstanceParameter::Any { + num_constraints: 2, + num_terms: 2, + max_id: 5, + max_degree: 2 + })) { + let parametric_instance: ParametricInstance = instance.clone().into(); + let converted_instance: Instance = parametric_instance.with_parameters(Parameters::default()).unwrap(); + prop_assert_eq!(instance, converted_instance); + } + } } From a96dfaa2fefb329bc9fe837d92efe9582d632fdd Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Fri, 22 Nov 2024 20:10:59 +0900 Subject: [PATCH 25/29] Manually implemented Arbitrary for Description --- Cargo.lock | 12 ------------ Cargo.toml | 1 - rust/ommx/Cargo.toml | 1 - rust/ommx/src/convert/instance.rs | 21 +++++++++++++++++++++ rust/ommx/src/ommx.v1.rs | 1 - rust/protogen/src/main.rs | 4 ---- 6 files changed, 21 insertions(+), 19 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a18d1503..066f3c39 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1327,7 +1327,6 @@ dependencies = [ "num", "ocipkg", "proptest", - "proptest-derive", "prost", "rand", "rand_xoshiro", @@ -1487,17 +1486,6 @@ dependencies = [ "unarray", ] -[[package]] -name = "proptest-derive" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ff7ff745a347b87471d859a377a9a404361e7efc2a971d73424a6d183c0fc77" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "prost" version = "0.12.6" diff --git a/Cargo.toml b/Cargo.toml index f3b796f9..6b31c917 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -33,7 +33,6 @@ maplit = "1.0.2" num = "0.4.3" ocipkg = "0.3.9" proptest = "1.5.0" -proptest-derive = "0.5.0" prost = "0.12.6" prost-build = "0.12.6" pyo3 = { version = "0.21.2", features = ["anyhow"] } diff --git a/rust/ommx/Cargo.toml b/rust/ommx/Cargo.toml index ff1d9976..4ecd88a7 100644 --- a/rust/ommx/Cargo.toml +++ b/rust/ommx/Cargo.toml @@ -31,7 +31,6 @@ maplit.workspace = true num.workspace = true ocipkg.workspace = true proptest.workspace = true -proptest-derive.workspace = true prost.workspace = true rand.workspace = true rand_xoshiro.workspace = true diff --git a/rust/ommx/src/convert/instance.rs b/rust/ommx/src/convert/instance.rs index f6435f69..ff39eb63 100644 --- a/rust/ommx/src/convert/instance.rs +++ b/rust/ommx/src/convert/instance.rs @@ -135,6 +135,27 @@ impl Arbitrary for Sense { } } +impl Arbitrary for Description { + type Parameters = (); + type Strategy = BoxedStrategy; + + fn arbitrary_with(_parameter: ()) -> Self::Strategy { + ( + Option::::arbitrary(), + Option::::arbitrary(), + prop_oneof![Just(Vec::new()), proptest::collection::vec(".*", 1..3)], + Option::::arbitrary(), + ) + .prop_map(|(name, description, authors, created_by)| Description { + name, + description, + authors, + created_by, + }) + .boxed() + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/rust/ommx/src/ommx.v1.rs b/rust/ommx/src/ommx.v1.rs index 07e381cc..5d43cf4f 100644 --- a/rust/ommx/src/ommx.v1.rs +++ b/rust/ommx/src/ommx.v1.rs @@ -328,7 +328,6 @@ pub struct Instance { /// Nested message and enum types in `Instance`. pub mod instance { #[non_exhaustive] - #[derive(::proptest_derive::Arbitrary)] #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct Description { diff --git a/rust/protogen/src/main.rs b/rust/protogen/src/main.rs index c0412f18..11a176c2 100644 --- a/rust/protogen/src/main.rs +++ b/rust/protogen/src/main.rs @@ -34,10 +34,6 @@ fn main() -> Result<()> { let mut cfg = Config::new(); cfg.type_attribute(".", "#[non_exhaustive]"); - cfg.type_attribute( - "ommx.v1.Instance.Description", - "#[derive(::proptest_derive::Arbitrary)]", - ); cfg.out_dir(&out).compile_protos(&protos, &[proto_root])?; std::process::Command::new("rustfmt") From afd6658b8e7ff31b590f76442d58047469fb1f72 Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Fri, 22 Nov 2024 20:14:34 +0900 Subject: [PATCH 26/29] Fix test --- rust/ommx/src/convert/parametric_instance.rs | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/rust/ommx/src/convert/parametric_instance.rs b/rust/ommx/src/convert/parametric_instance.rs index 67058dd4..efcf784f 100644 --- a/rust/ommx/src/convert/parametric_instance.rs +++ b/rust/ommx/src/convert/parametric_instance.rs @@ -103,21 +103,16 @@ impl ParametricInstance { #[cfg(test)] mod tests { - use crate::convert::instance::InstanceParameter; - use super::*; use proptest::prelude::*; proptest! { #[test] - fn test_parametric_instance_conversion(instance in Instance::arbitrary_with(InstanceParameter::Any { - num_constraints: 2, - num_terms: 2, - max_id: 5, - max_degree: 2 - })) { + fn test_parametric_instance_conversion(instance in Instance::arbitrary()) { let parametric_instance: ParametricInstance = instance.clone().into(); - let converted_instance: Instance = parametric_instance.with_parameters(Parameters::default()).unwrap(); + let mut converted_instance: Instance = parametric_instance.with_parameters(Parameters::default()).unwrap(); + prop_assert_eq!(converted_instance.parameters, Some(Parameters::default())); + converted_instance.parameters = None; prop_assert_eq!(instance, converted_instance); } } From b5c7aa9b0651ed7bfb760e8bd303fcf1f9db9e5b Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Mon, 25 Nov 2024 18:35:39 +0900 Subject: [PATCH 27/29] Test for parametric_instance conversion --- rust/ommx/src/convert.rs | 7 +++ rust/ommx/src/convert/constraint.rs | 20 +++++++ rust/ommx/src/convert/instance.rs | 62 +++++++++++++++++++- rust/ommx/src/convert/parametric_instance.rs | 11 ++-- 4 files changed, 95 insertions(+), 5 deletions(-) diff --git a/rust/ommx/src/convert.rs b/rust/ommx/src/convert.rs index 1d13df94..2f1eb97b 100644 --- a/rust/ommx/src/convert.rs +++ b/rust/ommx/src/convert.rs @@ -63,6 +63,13 @@ macro_rules! impl_neg_by_mul { self * -1.0 } } + + impl ::std::ops::Neg for &$ty { + type Output = $ty; + fn neg(self) -> Self::Output { + self.clone() * -1.0 + } + } }; } diff --git a/rust/ommx/src/convert/constraint.rs b/rust/ommx/src/convert/constraint.rs index e1d1bd19..1c719ec0 100644 --- a/rust/ommx/src/convert/constraint.rs +++ b/rust/ommx/src/convert/constraint.rs @@ -1,5 +1,6 @@ use crate::v1::{Constraint, Equality, Function}; use anyhow::{Context, Result}; +use approx::AbsDiffEq; use proptest::prelude::*; impl Constraint { @@ -10,6 +11,25 @@ impl Constraint { } } +impl AbsDiffEq for Constraint { + type Epsilon = f64; + + fn default_epsilon() -> Self::Epsilon { + f64::EPSILON + } + + fn abs_diff_eq(&self, other: &Self, epsilon: Self::Epsilon) -> bool { + if self.equality != other.equality { + return false; + } + if let (Some(f), Some(g)) = (&self.function, &other.function) { + f.abs_diff_eq(g, epsilon) + } else { + false + } + } +} + impl Arbitrary for Constraint { type Parameters = ::Parameters; type Strategy = BoxedStrategy; diff --git a/rust/ommx/src/convert/instance.rs b/rust/ommx/src/convert/instance.rs index ff39eb63..58fd2a1f 100644 --- a/rust/ommx/src/convert/instance.rs +++ b/rust/ommx/src/convert/instance.rs @@ -6,9 +6,10 @@ use crate::{ }, }; use anyhow::{bail, Context, Result}; +use approx::AbsDiffEq; use proptest::prelude::*; use rand::SeedableRng; -use std::collections::BTreeSet; +use std::collections::{BTreeMap, BTreeSet}; use super::{constraint::arbitrary_constraints, decision_variable::arbitrary_decision_variables}; @@ -156,6 +157,65 @@ impl Arbitrary for Description { } } +/// Compare two instances as mathematical programming problems. This does not compare the metadata. +/// +/// - This regards `min f` and `max -f` as the same problem. +/// - This cannot compare scaled constraints. For example, `2x + 3y <= 4` and `4x + 6y <= 8` are mathematically same, +/// but this regarded them as different problems. +/// +impl AbsDiffEq for Instance { + type Epsilon = f64; + + fn default_epsilon() -> Self::Epsilon { + f64::default_epsilon() + } + + fn abs_diff_eq(&self, other: &Self, epsilon: Self::Epsilon) -> bool { + let (Some(f), Some(g)) = (&self.objective, &other.objective) else { + // Return false if one of instance is invalid + return false; + }; + match (self.sense.try_into(), other.sense.try_into()) { + (Ok(Sense::Minimize), Ok(Sense::Minimize)) + | (Ok(Sense::Maximize), Ok(Sense::Maximize)) => { + if !f.abs_diff_eq(g, epsilon) { + return false; + } + } + (Ok(Sense::Minimize), Ok(Sense::Maximize)) + | (Ok(Sense::Maximize), Ok(Sense::Minimize)) => { + if !f.abs_diff_eq(&-g, epsilon) { + return false; + } + } + _ => return false, + } + + if self.constraints.len() != other.constraints.len() { + return false; + } + // The constraints may not ordered in the same way + let lhs = self + .constraints + .iter() + .map(|c| (c.id, (c.equality, c.function()))) + .collect::>(); + for c in &other.constraints { + if let (Some((eq, Ok(f))), Ok(g)) = (lhs.get(&c.id), c.function()) { + if *eq != c.equality as i32 { + return false; + } + if !(*f).abs_diff_eq(g, epsilon) { + return false; + } + } else { + return false; + } + } + true + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/rust/ommx/src/convert/parametric_instance.rs b/rust/ommx/src/convert/parametric_instance.rs index efcf784f..901f2ea5 100644 --- a/rust/ommx/src/convert/parametric_instance.rs +++ b/rust/ommx/src/convert/parametric_instance.rs @@ -104,16 +104,19 @@ impl ParametricInstance { #[cfg(test)] mod tests { use super::*; + use approx::abs_diff_eq; use proptest::prelude::*; proptest! { #[test] fn test_parametric_instance_conversion(instance in Instance::arbitrary()) { let parametric_instance: ParametricInstance = instance.clone().into(); - let mut converted_instance: Instance = parametric_instance.with_parameters(Parameters::default()).unwrap(); - prop_assert_eq!(converted_instance.parameters, Some(Parameters::default())); - converted_instance.parameters = None; - prop_assert_eq!(instance, converted_instance); + let converted_instance: Instance = parametric_instance.with_parameters(Parameters::default()).unwrap(); + prop_assert_eq!(&converted_instance.parameters, &Some(Parameters::default())); + prop_assert!( + abs_diff_eq!(instance, converted_instance, epsilon = 1e-10), + "\nLeft : {:?}\nRight: {:?}", instance, converted_instance + ); } } } From 93a8d318b0a7fdd3c92ea78373fd706dacfb1a3d Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Mon, 25 Nov 2024 19:36:29 +0900 Subject: [PATCH 28/29] clippy fix --- rust/ommx/src/convert/instance.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust/ommx/src/convert/instance.rs b/rust/ommx/src/convert/instance.rs index 58fd2a1f..5f104b3d 100644 --- a/rust/ommx/src/convert/instance.rs +++ b/rust/ommx/src/convert/instance.rs @@ -202,7 +202,7 @@ impl AbsDiffEq for Instance { .collect::>(); for c in &other.constraints { if let (Some((eq, Ok(f))), Ok(g)) = (lhs.get(&c.id), c.function()) { - if *eq != c.equality as i32 { + if *eq != c.equality { return false; } if !(*f).abs_diff_eq(g, epsilon) { From db95a549c4a6dc79132f2b0c1d07974aa2ba0735 Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Mon, 25 Nov 2024 20:31:49 +0900 Subject: [PATCH 29/29] Revise method names --- rust/ommx/src/convert/parametric_instance.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/rust/ommx/src/convert/parametric_instance.rs b/rust/ommx/src/convert/parametric_instance.rs index 901f2ea5..f662a1dc 100644 --- a/rust/ommx/src/convert/parametric_instance.rs +++ b/rust/ommx/src/convert/parametric_instance.rs @@ -81,6 +81,7 @@ impl ParametricInstance { .context("Objective function of ParametricInstance is empty") } + /// Used decision variable and parameter IDs in the objective and constraints. pub fn used_ids(&self) -> Result> { let mut used_ids = self.objective()?.used_decision_variable_ids(); for c in &self.constraints { @@ -89,14 +90,16 @@ impl ParametricInstance { Ok(used_ids) } - pub fn defined_ids(&self) -> BTreeSet { + /// Defined decision variable IDs. These IDs may not be used in the objective and constraints. + pub fn defined_decision_variable_ids(&self) -> BTreeSet { self.decision_variables .iter() .map(|dv| dv.id) .collect::>() } - pub fn parameter_ids(&self) -> BTreeSet { + /// Defined parameter IDs. These IDs may not be used in the objective and constraints. + pub fn defined_parameter_ids(&self) -> BTreeSet { self.parameters.iter().map(|p| p.id).collect() } }