├── LICENSE ├── README.md ├── qaekwy ├── __init__.py ├── __main__.py ├── engine.py ├── exception │ ├── __init__.py │ └── model_failure.py ├── explanation.py ├── model │ ├── __init__.py │ ├── constraint │ │ ├── __init__.py │ │ ├── abs.py │ │ ├── abstract_constraint.py │ │ ├── acos.py │ │ ├── asin.py │ │ ├── atan.py │ │ ├── cos.py │ │ ├── distinct.py │ │ ├── divide.py │ │ ├── element.py │ │ ├── exponential.py │ │ ├── logarithm.py │ │ ├── maximum.py │ │ ├── member.py │ │ ├── minimum.py │ │ ├── modulo.py │ │ ├── multiply.py │ │ ├── nroot.py │ │ ├── power.py │ │ ├── relational.py │ │ ├── sin.py │ │ ├── sort.py │ │ └── tan.py │ ├── cutoff.py │ ├── function.py │ ├── modeller.py │ ├── relation.py │ ├── searcher.py │ ├── specific.py │ └── variable │ │ ├── __init__.py │ │ ├── boolean.py │ │ ├── branch.py │ │ ├── float.py │ │ ├── integer.py │ │ └── variable.py ├── response.py └── solution.py ├── setup.py └── tests ├── model ├── constraint │ ├── test_abs.py │ ├── test_acos.py │ ├── test_asin.py │ ├── test_atan.py │ ├── test_cos.py │ ├── test_distinct.py │ ├── test_divide.py │ ├── test_element.py │ ├── test_exponential.py │ ├── test_logarithm.py │ ├── test_maximum.py │ ├── test_member.py │ ├── test_minimum.py │ ├── test_modulo.py │ ├── test_multiply.py │ ├── test_nroot.py │ ├── test_power.py │ ├── test_relational.py │ ├── test_sin.py │ ├── test_sort.py │ └── test_tan.py ├── test_modeller.py └── variable │ ├── test_expression.py │ └── test_integer.py ├── test_explanation.py └── test_solution.py /LICENSE: -------------------------------------------------------------------------------- 1 | EUROPEAN UNION PUBLIC LICENCE v. 1.2 2 | EUPL © the European Union 2007, 2016 3 | 4 | This European Union Public Licence (the ‘EUPL’) applies to the Work (as defined 5 | below) which is provided under the terms of this Licence. Any use of the Work, 6 | other than as authorised under this Licence is prohibited (to the extent such 7 | use is covered by a right of the copyright holder of the Work). 8 | 9 | The Work is provided under the terms of this Licence when the Licensor (as 10 | defined below) has placed the following notice immediately following the 11 | copyright notice for the Work: 12 | 13 | Licensed under the EUPL 14 | 15 | or has expressed by any other means his willingness to license under the EUPL. 16 | 17 | 1. Definitions 18 | 19 | In this Licence, the following terms have the following meaning: 20 | 21 | - ‘The Licence’: this Licence. 22 | 23 | - ‘The Original Work’: the work or software distributed or communicated by the 24 | Licensor under this Licence, available as Source Code and also as Executable 25 | Code as the case may be. 26 | 27 | - ‘Derivative Works’: the works or software that could be created by the 28 | Licensee, based upon the Original Work or modifications thereof. This Licence 29 | does not define the extent of modification or dependence on the Original Work 30 | required in order to classify a work as a Derivative Work; this extent is 31 | determined by copyright law applicable in the country mentioned in Article 15. 32 | 33 | - ‘The Work’: the Original Work or its Derivative Works. 34 | 35 | - ‘The Source Code’: the human-readable form of the Work which is the most 36 | convenient for people to study and modify. 37 | 38 | - ‘The Executable Code’: any code which has generally been compiled and which is 39 | meant to be interpreted by a computer as a program. 40 | 41 | - ‘The Licensor’: the natural or legal person that distributes or communicates 42 | the Work under the Licence. 43 | 44 | - ‘Contributor(s)’: any natural or legal person who modifies the Work under the 45 | Licence, or otherwise contributes to the creation of a Derivative Work. 46 | 47 | - ‘The Licensee’ or ‘You’: any natural or legal person who makes any usage of 48 | the Work under the terms of the Licence. 49 | 50 | - ‘Distribution’ or ‘Communication’: any act of selling, giving, lending, 51 | renting, distributing, communicating, transmitting, or otherwise making 52 | available, online or offline, copies of the Work or providing access to its 53 | essential functionalities at the disposal of any other natural or legal 54 | person. 55 | 56 | 2. Scope of the rights granted by the Licence 57 | 58 | The Licensor hereby grants You a worldwide, royalty-free, non-exclusive, 59 | sublicensable licence to do the following, for the duration of copyright vested 60 | in the Original Work: 61 | 62 | - use the Work in any circumstance and for all usage, 63 | - reproduce the Work, 64 | - modify the Work, and make Derivative Works based upon the Work, 65 | - communicate to the public, including the right to make available or display 66 | the Work or copies thereof to the public and perform publicly, as the case may 67 | be, the Work, 68 | - distribute the Work or copies thereof, 69 | - lend and rent the Work or copies thereof, 70 | - sublicense rights in the Work or copies thereof. 71 | 72 | Those rights can be exercised on any media, supports and formats, whether now 73 | known or later invented, as far as the applicable law permits so. 74 | 75 | In the countries where moral rights apply, the Licensor waives his right to 76 | exercise his moral right to the extent allowed by law in order to make effective 77 | the licence of the economic rights here above listed. 78 | 79 | The Licensor grants to the Licensee royalty-free, non-exclusive usage rights to 80 | any patents held by the Licensor, to the extent necessary to make use of the 81 | rights granted on the Work under this Licence. 82 | 83 | 3. Communication of the Source Code 84 | 85 | The Licensor may provide the Work either in its Source Code form, or as 86 | Executable Code. If the Work is provided as Executable Code, the Licensor 87 | provides in addition a machine-readable copy of the Source Code of the Work 88 | along with each copy of the Work that the Licensor distributes or indicates, in 89 | a notice following the copyright notice attached to the Work, a repository where 90 | the Source Code is easily and freely accessible for as long as the Licensor 91 | continues to distribute or communicate the Work. 92 | 93 | 4. Limitations on copyright 94 | 95 | Nothing in this Licence is intended to deprive the Licensee of the benefits from 96 | any exception or limitation to the exclusive rights of the rights owners in the 97 | Work, of the exhaustion of those rights or of other applicable limitations 98 | thereto. 99 | 100 | 5. Obligations of the Licensee 101 | 102 | The grant of the rights mentioned above is subject to some restrictions and 103 | obligations imposed on the Licensee. Those obligations are the following: 104 | 105 | Attribution right: The Licensee shall keep intact all copyright, patent or 106 | trademarks notices and all notices that refer to the Licence and to the 107 | disclaimer of warranties. The Licensee must include a copy of such notices and a 108 | copy of the Licence with every copy of the Work he/she distributes or 109 | communicates. The Licensee must cause any Derivative Work to carry prominent 110 | notices stating that the Work has been modified and the date of modification. 111 | 112 | Copyleft clause: If the Licensee distributes or communicates copies of the 113 | Original Works or Derivative Works, this Distribution or Communication will be 114 | done under the terms of this Licence or of a later version of this Licence 115 | unless the Original Work is expressly distributed only under this version of the 116 | Licence — for example by communicating ‘EUPL v. 1.2 only’. The Licensee 117 | (becoming Licensor) cannot offer or impose any additional terms or conditions on 118 | the Work or Derivative Work that alter or restrict the terms of the Licence. 119 | 120 | Compatibility clause: If the Licensee Distributes or Communicates Derivative 121 | Works or copies thereof based upon both the Work and another work licensed under 122 | a Compatible Licence, this Distribution or Communication can be done under the 123 | terms of this Compatible Licence. For the sake of this clause, ‘Compatible 124 | Licence’ refers to the licences listed in the appendix attached to this Licence. 125 | Should the Licensee's obligations under the Compatible Licence conflict with 126 | his/her obligations under this Licence, the obligations of the Compatible 127 | Licence shall prevail. 128 | 129 | Provision of Source Code: When distributing or communicating copies of the Work, 130 | the Licensee will provide a machine-readable copy of the Source Code or indicate 131 | a repository where this Source will be easily and freely available for as long 132 | as the Licensee continues to distribute or communicate the Work. 133 | 134 | Legal Protection: This Licence does not grant permission to use the trade names, 135 | trademarks, service marks, or names of the Licensor, except as required for 136 | reasonable and customary use in describing the origin of the Work and 137 | reproducing the content of the copyright notice. 138 | 139 | 6. Chain of Authorship 140 | 141 | The original Licensor warrants that the copyright in the Original Work granted 142 | hereunder is owned by him/her or licensed to him/her and that he/she has the 143 | power and authority to grant the Licence. 144 | 145 | Each Contributor warrants that the copyright in the modifications he/she brings 146 | to the Work are owned by him/her or licensed to him/her and that he/she has the 147 | power and authority to grant the Licence. 148 | 149 | Each time You accept the Licence, the original Licensor and subsequent 150 | Contributors grant You a licence to their contributions to the Work, under the 151 | terms of this Licence. 152 | 153 | 7. Disclaimer of Warranty 154 | 155 | The Work is a work in progress, which is continuously improved by numerous 156 | Contributors. It is not a finished work and may therefore contain defects or 157 | ‘bugs’ inherent to this type of development. 158 | 159 | For the above reason, the Work is provided under the Licence on an ‘as is’ basis 160 | and without warranties of any kind concerning the Work, including without 161 | limitation merchantability, fitness for a particular purpose, absence of defects 162 | or errors, accuracy, non-infringement of intellectual property rights other than 163 | copyright as stated in Article 6 of this Licence. 164 | 165 | This disclaimer of warranty is an essential part of the Licence and a condition 166 | for the grant of any rights to the Work. 167 | 168 | 8. Disclaimer of Liability 169 | 170 | Except in the cases of wilful misconduct or damages directly caused to natural 171 | persons, the Licensor will in no event be liable for any direct or indirect, 172 | material or moral, damages of any kind, arising out of the Licence or of the use 173 | of the Work, including without limitation, damages for loss of goodwill, work 174 | stoppage, computer failure or malfunction, loss of data or any commercial 175 | damage, even if the Licensor has been advised of the possibility of such damage. 176 | However, the Licensor will be liable under statutory product liability laws as 177 | far such laws apply to the Work. 178 | 179 | 9. Additional agreements 180 | 181 | While distributing the Work, You may choose to conclude an additional agreement, 182 | defining obligations or services consistent with this Licence. However, if 183 | accepting obligations, You may act only on your own behalf and on your sole 184 | responsibility, not on behalf of the original Licensor or any other Contributor, 185 | and only if You agree to indemnify, defend, and hold each Contributor harmless 186 | for any liability incurred by, or claims asserted against such Contributor by 187 | the fact You have accepted any warranty or additional liability. 188 | 189 | 10. Acceptance of the Licence 190 | 191 | The provisions of this Licence can be accepted by clicking on an icon ‘I agree’ 192 | placed under the bottom of a window displaying the text of this Licence or by 193 | affirming consent in any other similar way, in accordance with the rules of 194 | applicable law. Clicking on that icon indicates your clear and irrevocable 195 | acceptance of this Licence and all of its terms and conditions. 196 | 197 | Similarly, you irrevocably accept this Licence and all of its terms and 198 | conditions by exercising any rights granted to You by Article 2 of this Licence, 199 | such as the use of the Work, the creation by You of a Derivative Work or the 200 | Distribution or Communication by You of the Work or copies thereof. 201 | 202 | 11. Information to the public 203 | 204 | In case of any Distribution or Communication of the Work by means of electronic 205 | communication by You (for example, by offering to download the Work from a 206 | remote location) the distribution channel or media (for example, a website) must 207 | at least provide to the public the information requested by the applicable law 208 | regarding the Licensor, the Licence and the way it may be accessible, concluded, 209 | stored and reproduced by the Licensee. 210 | 211 | 12. Termination of the Licence 212 | 213 | The Licence and the rights granted hereunder will terminate automatically upon 214 | any breach by the Licensee of the terms of the Licence. 215 | 216 | Such a termination will not terminate the licences of any person who has 217 | received the Work from the Licensee under the Licence, provided such persons 218 | remain in full compliance with the Licence. 219 | 220 | 13. Miscellaneous 221 | 222 | Without prejudice of Article 9 above, the Licence represents the complete 223 | agreement between the Parties as to the Work. 224 | 225 | If any provision of the Licence is invalid or unenforceable under applicable 226 | law, this will not affect the validity or enforceability of the Licence as a 227 | whole. Such provision will be construed or reformed so as necessary to make it 228 | valid and enforceable. 229 | 230 | The European Commission may publish other linguistic versions or new versions of 231 | this Licence or updated versions of the Appendix, so far this is required and 232 | reasonable, without reducing the scope of the rights granted by the Licence. New 233 | versions of the Licence will be published with a unique version number. 234 | 235 | All linguistic versions of this Licence, approved by the European Commission, 236 | have identical value. Parties can take advantage of the linguistic version of 237 | their choice. 238 | 239 | 14. Jurisdiction 240 | 241 | Without prejudice to specific agreement between parties, 242 | 243 | - any litigation resulting from the interpretation of this License, arising 244 | between the European Union institutions, bodies, offices or agencies, as a 245 | Licensor, and any Licensee, will be subject to the jurisdiction of the Court 246 | of Justice of the European Union, as laid down in article 272 of the Treaty on 247 | the Functioning of the European Union, 248 | 249 | - any litigation arising between other parties and resulting from the 250 | interpretation of this License, will be subject to the exclusive jurisdiction 251 | of the competent court where the Licensor resides or conducts its primary 252 | business. 253 | 254 | 15. Applicable Law 255 | 256 | Without prejudice to specific agreement between parties, 257 | 258 | - this Licence shall be governed by the law of the European Union Member State 259 | where the Licensor has his seat, resides or has his registered office, 260 | 261 | - this licence shall be governed by Belgian law if the Licensor has no seat, 262 | residence or registered office inside a European Union Member State. 263 | 264 | Appendix 265 | 266 | ‘Compatible Licences’ according to Article 5 EUPL are: 267 | 268 | - GNU General Public License (GPL) v. 2, v. 3 269 | - GNU Affero General Public License (AGPL) v. 3 270 | - Open Software License (OSL) v. 2.1, v. 3.0 271 | - Eclipse Public License (EPL) v. 1.0 272 | - CeCILL v. 2.0, v. 2.1 273 | - Mozilla Public Licence (MPL) v. 2 274 | - GNU Lesser General Public Licence (LGPL) v. 2.1, v. 3 275 | - Creative Commons Attribution-ShareAlike v. 3.0 Unported (CC BY-SA 3.0) for 276 | works other than software 277 | - European Union Public Licence (EUPL) v. 1.1, v. 1.2 278 | - Québec Free and Open-Source Licence — Reciprocity (LiLiQ-R) or Strong 279 | Reciprocity (LiLiQ-R+). 280 | 281 | The European Commission may update this Appendix to later versions of the above 282 | licences without producing a new version of the EUPL, as long as they provide 283 | the rights granted in Article 2 of this Licence and protect the covered Source 284 | Code from exclusive appropriation. 285 | 286 | All other changes or additions to this Appendix require the production of a new 287 | EUPL version. 288 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Qaekwy Python Client 2 | 3 | *Operational Research at your fingertips.* 4 | 5 | The Qaekwy Python Client serves as a powerful tool to interact with the Qaekwy optimization 6 | solver engine through its API. This client provides a convenient and programmatic way to 7 | **create**, **model**, and **solve** optimization problems using Qaekwy, streamlining 8 | the process of **formulating complex problems and finding optimal solutions**. 9 | 10 | Qaekwy is small optimization problem solver engine designed to tackle a wide range of 11 | real-world challenges. It provides powerful modeling capabilities and efficient solving 12 | algorithms to find optimal solutions to complex problems. 13 | 14 | 15 | ## Features 16 | 17 | - **Modeling Made Easy:** Define variables, constraints, and objective functions seamlessly. 18 | Qaekwy's `Modeller` class helps you create optimization models with clarity and precision. 19 | 20 | - **Diverse Constraint Support:** Qaekwy supports various constraint types, from simple arithmetic 21 | to complex mathematical expressions. Create constraints that accurately represent real-world scenarios. 22 | 23 | - **Effortless Optimization:** Qaekwy abstracts away the complexities of communication with 24 | optimization engine. Send requests and receive responses using intuitive methods. 25 | 26 | - **Flexibility**: You can leverage the Qaekwy Python Client to tailor optimization problems to their specific 27 | needs by utilizing Qaekwy's modelling capabilities. This includes handling various types of constraints, 28 | objectives, and solver algorithms. 29 | 30 | 31 | ## Installation 32 | 33 | ```shell 34 | pip install qaekwy 35 | ``` 36 | 37 | 38 | ## Documentation 39 | 40 | Explore the [Qaekwy Documentation](https://docs.qaekwy.io) for in-depth guides, examples, and usage details. 41 | 42 | 43 | ## Example 44 | 45 | How to use the Qaekwy Python Client to solve a very small optimization problem: 46 | 47 | ```python 48 | from qaekwy.engine import DirectEngine 49 | from qaekwy.model.constraint.relational import RelationalExpression 50 | from qaekwy.model.specific import SpecificMaximum 51 | from qaekwy.model.variable.integer import IntegerVariable 52 | from qaekwy.model.modeller import Modeller 53 | from qaekwy.model.searcher import SearcherType 54 | 55 | # Define the optimization problem using Qaekwy Python Client 56 | class SimpleOptimizationProblem(Modeller): 57 | def __init__(self): 58 | super().__init__() 59 | 60 | # Create a integer variables 61 | x = IntegerVariable(var_name="x", domain_low=0, domain_high=10) 62 | y = IntegerVariable(var_name="y", domain_low=0, domain_high=10) 63 | z = IntegerVariable(var_name="z", domain_low=0, domain_high=10) 64 | 65 | # Constraints 66 | constraint_1 = RelationalExpression(y > 2 * x) 67 | constraint_2 = RelationalExpression(x >= 4) 68 | constraint_3 = RelationalExpression(z == y - x) 69 | 70 | # Objective: Maximize z 71 | self.add_objective( 72 | SpecificMaximum(variable=z) 73 | ) 74 | 75 | # Add variable and constraint to the problem 76 | self.add_variable(x) 77 | self.add_variable(y) 78 | self.add_variable(z) 79 | self.add_constraint(constraint_1) 80 | self.add_constraint(constraint_2) 81 | self.add_constraint(constraint_3) 82 | 83 | # Set the search strategy 84 | self.set_searcher(SearcherType.BAB) 85 | 86 | # Create a Qaekwy engine for interaction with the freely-available Cloud instance 87 | qaekwy_engine = DirectEngine() 88 | 89 | # Create the optimization problem instance 90 | optimization_problem = SimpleOptimizationProblem() 91 | 92 | # Request the Qaekwy engine to solve the problem 93 | response = qaekwy_engine.model(model=optimization_problem) 94 | 95 | # Retrieve the list of solutions from the response 96 | list_of_solutions = response.get_solutions() 97 | 98 | # Print the solution(s) obtained 99 | for solution in list_of_solutions: 100 | print(f"Optimal solution: x = {solution.x}") 101 | print(f"Optimal solution: y = {solution.y}") 102 | print(f"Optimal solution: z = {solution.z}") 103 | ``` 104 | 105 | Output: 106 | 107 | ``` 108 | Optimal solution: x = 4 109 | Optimal solution: y = 10 110 | Optimal solution: z = 6 111 | ``` 112 | 113 | ## License 114 | 115 | This software is licensed under the **European Union Public License v1.2** 116 | -------------------------------------------------------------------------------- /qaekwy/__init__.py: -------------------------------------------------------------------------------- 1 | """ Qaekwy """ 2 | 3 | __copyright__ = "©2023" 4 | __license__ = "European Union Public Licence 1.2 (EUPL 1.2)" 5 | __software__ = "Qaekwy" 6 | __author__ = "Alexis LE GOADEC" 7 | __author_email__ = "alex@qaekwy.io" 8 | __version__ = "0.1.4" 9 | -------------------------------------------------------------------------------- /qaekwy/__main__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Main Module 3 | """ 4 | 5 | import argparse 6 | from . import __software__, __version__, __author__, __copyright__, __license__ 7 | 8 | if __name__ == "__main__": 9 | parser = argparse.ArgumentParser(description="Qaekwy Python Library") 10 | parser.add_argument( 11 | "-v", 12 | "--version", 13 | action="store_true", 14 | help="Display the version of the library", 15 | ) 16 | 17 | args = parser.parse_args() 18 | if args.version: 19 | print(f"{__copyright__} {__author__} - {__software__}, v{__version__}") 20 | print(f"This software library is licensed under the {__license__}") 21 | -------------------------------------------------------------------------------- /qaekwy/exception/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alex-87/qaekwy-python/c4a76740e69d295ec55d0dc5ebfc8355d6e7a679/qaekwy/exception/__init__.py -------------------------------------------------------------------------------- /qaekwy/exception/model_failure.py: -------------------------------------------------------------------------------- 1 | """Model Failure Exception 2 | 3 | This module defines the ModelFailure class, which represents an 4 | exception that can be raised when a failure occurs in a model. 5 | 6 | Classes: 7 | ModelFailure: Represents an exception raised when a model failure occurs. 8 | 9 | """ 10 | 11 | 12 | class ModelFailure(Exception): 13 | """ 14 | The ModelFailure class extends the built-in Exception class to represent 15 | an exceptional situation where a failure occurs in a model. 16 | 17 | It can be raised to indicate unexpected or erroneous conditions in 18 | the building of a model. 19 | """ 20 | 21 | def __init__(self, *args: object) -> None: 22 | super().__init__(*args) 23 | -------------------------------------------------------------------------------- /qaekwy/explanation.py: -------------------------------------------------------------------------------- 1 | """Explanation Class 2 | 3 | This module defines the Explanation class, which represents an explanation for a model. 4 | 5 | Classes: 6 | Explanation: Represents an explanation for a model. 7 | 8 | """ 9 | 10 | 11 | class Explanation: 12 | """ 13 | Represents an explanation for a model. 14 | 15 | The Explanation class provides methods to extract variable and constraint explanations 16 | from the explanation JSON content. It allows users to easily retrieve explanations for 17 | variables and constraints provided in the explanation content. 18 | 19 | Attributes: 20 | explanation_content (list): A list containing JSON content representing the explanation. 21 | 22 | Methods: 23 | __init__(explanation_content): Initialize an Explanation instance with JSON content. 24 | get_variables(): Extract and return explanations for variables. 25 | get_constraints(): Extract and return explanations for constraints. 26 | """ 27 | 28 | def __init__(self, explanation_content: list) -> None: 29 | self.explanation_content = explanation_content 30 | 31 | def get_variables(self) -> dict: 32 | """ 33 | Extract and return explanations for variables. 34 | 35 | Returns: 36 | dict: A dictionary containing variable explanations with variable names as keys. 37 | 38 | """ 39 | dico_final = {} 40 | for element in self.explanation_content: 41 | variable_name = element["name"] 42 | variable_type = element["type"] 43 | variable_expl = element["explanation"] 44 | 45 | if element["type"] == "var": 46 | dico_final[variable_name] = { 47 | "type": variable_type, 48 | "explanation": variable_expl, 49 | } 50 | 51 | return dico_final 52 | 53 | def get_constraints(self) -> dict: 54 | """ 55 | Extract and return explanations for constraints. 56 | 57 | Returns: 58 | dict: A dictionary containing constraint explanations with constraint names as keys. 59 | 60 | """ 61 | dico_final = {} 62 | for element in self.explanation_content: 63 | constraint_name = element["name"] 64 | constraint_type = element["type"] 65 | constraint_expl = element["explanation"] 66 | 67 | if element["type"] == "constraint": 68 | dico_final[constraint_name] = { 69 | "type": constraint_type, 70 | "explanation": constraint_expl, 71 | } 72 | 73 | return dico_final 74 | -------------------------------------------------------------------------------- /qaekwy/model/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Initialization Module for Model 3 | """ 4 | 5 | DIRECTENGINE_API_ENDPOINT = "https://demo-api.qaekwy.io/" 6 | -------------------------------------------------------------------------------- /qaekwy/model/constraint/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alex-87/qaekwy-python/c4a76740e69d295ec55d0dc5ebfc8355d6e7a679/qaekwy/model/constraint/__init__.py -------------------------------------------------------------------------------- /qaekwy/model/constraint/abs.py: -------------------------------------------------------------------------------- 1 | """ConstraintAbs Module 2 | 3 | This module defines the ConstraintAbs class, which represents an absolute value constraint 4 | between two variables. 5 | 6 | Classes: 7 | ConstraintAbs: Represents an absolute value constraint between two variables. 8 | 9 | """ 10 | 11 | from qaekwy.model.constraint.abstract_constraint import AbstractConstraint 12 | from qaekwy.model.variable.variable import Variable 13 | 14 | 15 | class ConstraintAbs(AbstractConstraint): 16 | """ 17 | Represents an absolute value constraint between two variables. 18 | 19 | This constraint ensures that the absolute value of var_1 is equal to var_2 20 | 21 | Args: 22 | var_1 (Variable): The first variable in the constraint. 23 | var_2 (Variable): The second variable in the constraint. 24 | constraint_name (str, optional): A name for the constraint. 25 | 26 | Attributes: 27 | var_1 (Variable): The first variable in the constraint. 28 | var_2 (Variable): The second variable in the constraint. 29 | 30 | Methods: 31 | to_json(): Returns a JSON representation of the constraint. 32 | 33 | Example: 34 | 35 | """ 36 | 37 | def __init__(self, var_1: Variable, var_2: Variable, constraint_name=None) -> None: 38 | """ 39 | Initialize a new absolute value constraint instance. 40 | 41 | Args: 42 | var_1 (Variable): The first variable in the constraint. 43 | var_2 (Variable): The second variable in the constraint. 44 | constraint_name (str, optional): A name for the constraint. 45 | """ 46 | super().__init__(constraint_name) 47 | self.var_1 = var_1 48 | self.var_2 = var_2 49 | 50 | def to_json(self) -> dict: 51 | return { 52 | "name": self.constraint_name, 53 | "v1": self.var_1.var_name, 54 | "v2": self.var_2.var_name, 55 | "type": "abs", 56 | } 57 | -------------------------------------------------------------------------------- /qaekwy/model/constraint/abstract_constraint.py: -------------------------------------------------------------------------------- 1 | """Abstract Constraint Class 2 | 3 | This module defines the AbstractConstraint class, an abstract 4 | base class for representing constraints in a modelling. 5 | 6 | Classes: 7 | AbstractConstraint: Represents an abstract constraint. 8 | 9 | """ 10 | 11 | from abc import ABC, abstractmethod 12 | 13 | import random 14 | import string 15 | 16 | 17 | class AbstractConstraint(ABC): 18 | """ 19 | Represents an abstract constraint. 20 | 21 | The AbstractConstraint class serves as an abstract base class for defining 22 | constraints within a modelling. Constraints encapsulate relationships 23 | and rules that must be satisfied within the model. 24 | 25 | Attributes: 26 | constraint_name (str): The name of the constraint. 27 | 28 | Methods: 29 | random_constraint_name(): Generates a random constraint name. 30 | to_json(): Converts the constraint to a JSON representation. 31 | 32 | """ 33 | 34 | def random_constraint_name(self) -> str: 35 | """ 36 | Generate a random constraint name. 37 | 38 | Returns: 39 | str: A randomly generated constraint name. 40 | """ 41 | return "".join( 42 | random.choices( 43 | string.ascii_lowercase + string.ascii_uppercase + string.digits, k=16 44 | ) 45 | ) 46 | 47 | def __init__(self, constraint_name) -> None: 48 | """ 49 | Initialize an AbstractConstraint instance. 50 | 51 | Args: 52 | constraint_name (str): The name of the constraint. If None, a random name is generated. 53 | 54 | """ 55 | self.constraint_name = ( 56 | constraint_name 57 | if constraint_name is not None 58 | else self.random_constraint_name() 59 | ) 60 | 61 | @abstractmethod 62 | def to_json(self): 63 | """ 64 | Convert the constraint to a JSON representation. 65 | 66 | This method must be implemented in concrete subclasses to provide 67 | a JSON representation of the constraint. 68 | 69 | Returns: 70 | dict: A dictionary representing the constraint in JSON format. 71 | 72 | """ 73 | return {} 74 | -------------------------------------------------------------------------------- /qaekwy/model/constraint/acos.py: -------------------------------------------------------------------------------- 1 | """ConstraintACos Module 2 | 3 | This module defines the ConstraintACos class, which represents an arccosine constraint 4 | between two variables. 5 | 6 | Classes: 7 | ConstraintACos: Represents an arccosine constraint between two variables. 8 | 9 | """ 10 | 11 | from qaekwy.model.constraint.abstract_constraint import AbstractConstraint 12 | from qaekwy.model.variable.variable import Variable 13 | 14 | 15 | class ConstraintACos(AbstractConstraint): 16 | """ 17 | Represents an arccosine constraint between two variables. 18 | 19 | This constraint enforces the relationship between the arccosine of var_1 and var_2. 20 | It ensures that the arccosine of var_1 is equal to var_2. 21 | 22 | Args: 23 | var_1 (Variable): The first variable in the constraint. 24 | var_2 (Variable): The second variable in the constraint. 25 | constraint_name (str, optional): A name for the constraint. 26 | 27 | Attributes: 28 | var_1 (Variable): The first variable in the constraint. 29 | var_2 (Variable): The second variable in the constraint. 30 | 31 | Methods: 32 | to_json(): Returns a JSON representation of the constraint. 33 | 34 | Example: 35 | acos_constraint = ConstraintACos(var_angle, var_value, "acos_constraint") 36 | """ 37 | 38 | def __init__(self, var_1: Variable, var_2: Variable, constraint_name=None) -> None: 39 | """ 40 | Initialize a new arccosine constraint instance. 41 | 42 | Args: 43 | var_1 (Variable): The first variable in the constraint. 44 | var_2 (Variable): The second variable in the constraint. 45 | constraint_name (str, optional): A name for the constraint. 46 | """ 47 | super().__init__(constraint_name) 48 | self.var_1 = var_1 49 | self.var_2 = var_2 50 | 51 | def to_json(self) -> dict: 52 | """ 53 | Convert the constraint to a JSON representation. 54 | 55 | Returns: 56 | dict: A dictionary containing constraint information in JSON format. 57 | """ 58 | return { 59 | "name": self.constraint_name, 60 | "v1": self.var_1.var_name, 61 | "v2": self.var_2.var_name, 62 | "type": "acos", 63 | } 64 | -------------------------------------------------------------------------------- /qaekwy/model/constraint/asin.py: -------------------------------------------------------------------------------- 1 | """ConstraintASin Module 2 | 3 | This module defines the ConstraintASin class, which represents an arcsine constraint 4 | between two variables. 5 | 6 | Classes: 7 | ConstraintASin: Represents an arcsine constraint between two variables. 8 | 9 | """ 10 | 11 | from qaekwy.model.constraint.abstract_constraint import AbstractConstraint 12 | from qaekwy.model.variable.variable import Variable 13 | 14 | 15 | class ConstraintASin(AbstractConstraint): 16 | """ 17 | Represents an arcsine constraint between two variables. 18 | 19 | This constraint enforces the relationship between the arcsine of var_1 and var_2. 20 | It ensures that the arcsine of var_1 is equal to var_2,. 21 | 22 | Args: 23 | var_1 (Variable): The first variable in the constraint. 24 | var_2 (Variable): The second variable in the constraint. 25 | constraint_name (str, optional): A name for the constraint. 26 | 27 | Attributes: 28 | var_1 (Variable): The first variable in the constraint. 29 | var_2 (Variable): The second variable in the constraint. 30 | 31 | Methods: 32 | to_json(): Returns a JSON representation of the constraint. 33 | 34 | Example: 35 | asin_constraint = ConstraintASin(var_angle, var_value, "asin_constraint") 36 | """ 37 | 38 | def __init__(self, var_1: Variable, var_2: Variable, constraint_name=None) -> None: 39 | """ 40 | Initialize a new arcsine constraint instance. 41 | 42 | Args: 43 | var_1 (Variable): The first variable in the constraint. 44 | var_2 (Variable): The second variable in the constraint. 45 | constraint_name (str, optional): A name for the constraint. 46 | """ 47 | super().__init__(constraint_name) 48 | self.var_1 = var_1 49 | self.var_2 = var_2 50 | 51 | def to_json(self) -> dict: 52 | """ 53 | Convert the constraint to a JSON representation. 54 | 55 | Returns: 56 | dict: A dictionary containing constraint information in JSON format. 57 | """ 58 | return { 59 | "name": self.constraint_name, 60 | "v1": self.var_1.var_name, 61 | "v2": self.var_2.var_name, 62 | "type": "asin", 63 | } 64 | -------------------------------------------------------------------------------- /qaekwy/model/constraint/atan.py: -------------------------------------------------------------------------------- 1 | """ConstraintATan Module 2 | 3 | This module defines the ConstraintATan class, which represents an arctangent constraint 4 | between two variables. 5 | 6 | Classes: 7 | ConstraintATan: Represents an arctangent constraint between two variables. 8 | 9 | """ 10 | 11 | from qaekwy.model.constraint.abstract_constraint import AbstractConstraint 12 | from qaekwy.model.variable.variable import Variable 13 | 14 | 15 | class ConstraintATan(AbstractConstraint): 16 | """ 17 | Represents an arctangent constraint between two variables. 18 | 19 | This constraint enforces the relationship between the arctangent of var_1 and var_2. 20 | It ensures that the arctangent of var_1 is equal to var_2. 21 | 22 | Args: 23 | var_1 (Variable): The first variable in the constraint. 24 | var_2 (Variable): The second variable in the constraint. 25 | constraint_name (str, optional): A name for the constraint. 26 | 27 | Attributes: 28 | var_1 (Variable): The first variable in the constraint. 29 | var_2 (Variable): The second variable in the constraint. 30 | 31 | Methods: 32 | to_json(): Returns a JSON representation of the constraint. 33 | 34 | Example: 35 | atan_constraint = ConstraintATan(var_angle, var_value, "atan_constraint") 36 | """ 37 | 38 | def __init__(self, var_1: Variable, var_2: Variable, constraint_name=None) -> None: 39 | """ 40 | Initialize a new arctangent constraint instance. 41 | 42 | Args: 43 | var_1 (Variable): The first variable in the constraint. 44 | var_2 (Variable): The second variable in the constraint. 45 | constraint_name (str, optional): A name for the constraint. 46 | """ 47 | super().__init__(constraint_name) 48 | self.var_1 = var_1 49 | self.var_2 = var_2 50 | 51 | def to_json(self) -> dict: 52 | """ 53 | Convert the constraint to a JSON representation. 54 | 55 | Returns: 56 | dict: A dictionary containing constraint information in JSON format. 57 | """ 58 | return { 59 | "name": self.constraint_name, 60 | "v1": self.var_1.var_name, 61 | "v2": self.var_2.var_name, 62 | "type": "atan", 63 | } 64 | -------------------------------------------------------------------------------- /qaekwy/model/constraint/cos.py: -------------------------------------------------------------------------------- 1 | """ConstraintCos Module 2 | 3 | This module defines the ConstraintCos class, which represents a cosine constraint 4 | between two variables. 5 | 6 | Classes: 7 | ConstraintCos: Represents a cosine constraint between two variables. 8 | 9 | """ 10 | 11 | from qaekwy.model.constraint.abstract_constraint import AbstractConstraint 12 | from qaekwy.model.variable.variable import Variable 13 | 14 | 15 | class ConstraintCos(AbstractConstraint): 16 | """ 17 | Represents a cosine constraint between two variables. 18 | 19 | This constraint enforces the relationship between the cosine of var_1 and var_2. 20 | It ensures that the cosine of var_1 is equal to var_2. 21 | 22 | Args: 23 | var_1 (Variable): The first variable in the constraint. 24 | var_2 (Variable): The second variable in the constraint. 25 | constraint_name (str, optional): A name for the constraint. 26 | 27 | Attributes: 28 | var_1 (Variable): The first variable in the constraint. 29 | var_2 (Variable): The second variable in the constraint. 30 | 31 | Methods: 32 | to_json(): Returns a JSON representation of the constraint. 33 | 34 | Example: 35 | cos_constraint = ConstraintCos(var_angle, var_value, "cos_constraint") 36 | """ 37 | 38 | def __init__(self, var_1: Variable, var_2: Variable, constraint_name=None) -> None: 39 | """ 40 | Initialize a new cosine constraint instance. 41 | 42 | Args: 43 | var_1 (Variable): The first variable in the constraint. 44 | var_2 (Variable): The second variable in the constraint. 45 | constraint_name (str, optional): A name for the constraint. 46 | """ 47 | super().__init__(constraint_name) 48 | self.var_1 = var_1 49 | self.var_2 = var_2 50 | 51 | def to_json(self) -> dict: 52 | """ 53 | Convert the constraint to a JSON representation. 54 | 55 | Returns: 56 | dict: A dictionary containing constraint information in JSON format. 57 | """ 58 | return { 59 | "name": self.constraint_name, 60 | "v1": self.var_1.var_name, 61 | "v2": self.var_2.var_name, 62 | "type": "cos", 63 | } 64 | -------------------------------------------------------------------------------- /qaekwy/model/constraint/distinct.py: -------------------------------------------------------------------------------- 1 | """ConstraintDistinct Module 2 | 3 | This module defines constraint classes for enforcing distinctness in arrays or 4 | matrices. 5 | 6 | Classes: 7 | ConstraintDistinctArray: 8 | Represents a constraint to ensure distinctness within an array. 9 | 10 | ConstraintDistinctRow: 11 | Represents a constraint to ensure distinctness within a specific row of an array. 12 | 13 | ConstraintDistinctCol: 14 | Represents a constraint to ensure distinctness within a specific column of an array. 15 | 16 | ConstraintDistinctSlice: 17 | Represents a constraint to ensure distinctness within a specific slice of an array. 18 | 19 | """ 20 | 21 | from qaekwy.model.constraint.abstract_constraint import AbstractConstraint 22 | from qaekwy.model.variable.variable import ArrayVariable 23 | 24 | 25 | class ConstraintDistinctArray(AbstractConstraint): 26 | """ 27 | Represents a constraint to ensure distinctness within an array. 28 | 29 | This constraint enforces that all elements within the given array variable are distinct. 30 | 31 | Args: 32 | var_1 (ArrayVariable): The array variable to enforce distinctness for. 33 | constraint_name (str, optional): A name for the constraint. 34 | 35 | Attributes: 36 | var_1 (ArrayVariable): The array variable to enforce distinctness for. 37 | 38 | Methods: 39 | to_json(): Returns a JSON representation of the constraint. 40 | 41 | Example: 42 | distinct_constraint = ConstraintDistinctArray(array_var, "distinct_array_constraint") 43 | """ 44 | 45 | def __init__(self, var_1: ArrayVariable, constraint_name=None) -> None: 46 | """ 47 | Initialize a new distinct array constraint instance. 48 | 49 | Args: 50 | var_1 (ArrayVariable): The array variable to enforce distinctness for. 51 | constraint_name (str, optional): A name for the constraint. 52 | """ 53 | super().__init__(constraint_name) 54 | self.var_1 = var_1 55 | 56 | def to_json(self): 57 | """ 58 | Convert the constraint to a JSON representation. 59 | 60 | Returns: 61 | dict: A dictionary containing constraint information in JSON format. 62 | """ 63 | return { 64 | "name": self.constraint_name, 65 | "type": "distinct", 66 | "v1": self.var_1.var_name, 67 | "selection": "standard", 68 | } 69 | 70 | 71 | class ConstraintDistinctRow(AbstractConstraint): 72 | """ 73 | Represents a constraint to ensure distinctness within a specific row of an array. 74 | 75 | This constraint enforces that all elements within a designated row of the given array variable 76 | are distinct from each other. 77 | 78 | Args: 79 | var_1 (ArrayVariable): The array variable to enforce distinctness within a row. 80 | size (int): The number of elements in the row to ensure distinctness for. 81 | idx (int): The index of the row to enforce distinctness for. 82 | constraint_name (str, optional): A name for the constraint. 83 | 84 | Attributes: 85 | var_1 (ArrayVariable): The array variable to enforce distinctness within a row. 86 | size (int): The number of elements in the row to ensure distinctness for. 87 | idx (int): The index of the row to enforce distinctness for. 88 | 89 | Methods: 90 | to_json(): Returns a JSON representation of the constraint. 91 | 92 | Example: 93 | distinct_row_constraint = 94 | ConstraintDistinctRow(array_var, size=3, idx=1, constraint_name="distinct_row_constraint") 95 | """ 96 | 97 | def __init__( 98 | self, var_1: ArrayVariable, size: int, idx: int, constraint_name=None 99 | ) -> None: 100 | """ 101 | Initialize a new distinct row constraint instance. 102 | 103 | Args: 104 | var_1 (ArrayVariable): The array variable to enforce distinctness within a row. 105 | size (int): The number of elements in the row to ensure distinctness for. 106 | idx (int): The index of the row to enforce distinctness for. 107 | constraint_name (str, optional): A name for the constraint. 108 | """ 109 | super().__init__(constraint_name) 110 | self.var_1 = var_1 111 | self.size = size 112 | self.idx = idx 113 | 114 | def to_json(self): 115 | """ 116 | Convert the constraint to a JSON representation. 117 | 118 | Returns: 119 | dict: A dictionary containing constraint information in JSON format. 120 | """ 121 | return { 122 | "name": self.constraint_name, 123 | "type": "distinct", 124 | "v1": self.var_1.var_name, 125 | "selection": "row", 126 | "size": self.size, 127 | "index": self.idx, 128 | } 129 | 130 | 131 | class ConstraintDistinctCol(AbstractConstraint): 132 | """ 133 | Represents a constraint to ensure distinctness within a specific column of an array. 134 | 135 | This constraint enforces that all elements within a designated column of 136 | the given array variable are distinct from each other. 137 | 138 | Args: 139 | var_1 (ArrayVariable): The array variable to enforce distinctness within a column. 140 | size (int): The number of elements in the column to ensure distinctness for. 141 | idx (int): The index of the column to enforce distinctness for. 142 | constraint_name (str, optional): A name for the constraint. 143 | 144 | Attributes: 145 | var_1 (ArrayVariable): The array variable to enforce distinctness within a column. 146 | size (int): The number of elements in the column to ensure distinctness for. 147 | idx (int): The index of the column to enforce distinctness for. 148 | 149 | Methods: 150 | to_json(): Returns a JSON representation of the constraint. 151 | 152 | Example: 153 | distinct_col_constraint = 154 | ConstraintDistinctCol(array_var, size=3, idx=0, constraint_name="distinct_col_constraint") 155 | """ 156 | 157 | def __init__( 158 | self, var_1: ArrayVariable, size: int, idx: int, constraint_name=None 159 | ) -> None: 160 | """ 161 | Initialize a new distinct column constraint instance. 162 | 163 | Args: 164 | var_1 (ArrayVariable): The array variable to enforce distinctness within a column. 165 | size (int): The number of elements in the column to ensure distinctness for. 166 | idx (int): The index of the column to enforce distinctness for. 167 | constraint_name (str, optional): A name for the constraint. 168 | """ 169 | super().__init__(constraint_name) 170 | self.var_1 = var_1 171 | self.size = size 172 | self.idx = idx 173 | 174 | def to_json(self): 175 | """ 176 | Convert the constraint to a JSON representation. 177 | 178 | Returns: 179 | dict: A dictionary containing constraint information in JSON format. 180 | """ 181 | return { 182 | "name": self.constraint_name, 183 | "type": "distinct", 184 | "v1": self.var_1.var_name, 185 | "selection": "col", 186 | "size": self.size, 187 | "index": self.idx, 188 | } 189 | 190 | 191 | class ConstraintDistinctSlice(AbstractConstraint): 192 | """ 193 | Represents a constraint to ensure distinctness within a specific slice of an array. 194 | 195 | This constraint enforces that all elements within a designated rectangular 196 | slice of the given array variable are distinct from each other. 197 | 198 | Args: 199 | var_1 (ArrayVariable): The array variable to enforce distinctness within a slice. 200 | size (int): The number of elements in the slice to ensure distinctness for. 201 | offset_start_x (int): The starting offset along the x-axis for the slice. 202 | offset_start_y (int): The starting offset along the y-axis for the slice. 203 | offset_end_x (int): The ending offset along the x-axis for the slice. 204 | offset_end_y (int): The ending offset along the y-axis for the slice. 205 | constraint_name (str, optional): A name for the constraint. 206 | 207 | Attributes: 208 | var_1 (ArrayVariable): The array variable to enforce distinctness within a slice. 209 | size (int): The number of elements in the slice to ensure distinctness for. 210 | offset_start_x (int): The starting offset along the x-axis for the slice. 211 | offset_start_y (int): The starting offset along the y-axis for the slice. 212 | offset_end_x (int): The ending offset along the x-axis for the slice. 213 | offset_end_y (int): The ending offset along the y-axis for the slice. 214 | 215 | Methods: 216 | to_json(): Returns a JSON representation of the constraint. 217 | 218 | Example: 219 | distinct_slice_constraint = 220 | ConstraintDistinctSlice( 221 | array_var, 222 | size=6, 223 | offset_start_x=1, 224 | offset_start_y=1, 225 | offset_end_x=3, 226 | offset_end_y=2, 227 | constraint_name="distinct_slice_constraint") 228 | """ 229 | 230 | def __init__( # pylint: disable=too-many-arguments 231 | self, 232 | var_1: ArrayVariable, 233 | size: int, 234 | offset_start_x: int, 235 | offset_start_y: int, 236 | offset_end_x: int, 237 | offset_end_y: int, 238 | constraint_name=None, 239 | ) -> None: 240 | """ 241 | Initialize a new distinct slice constraint instance. 242 | 243 | Args: 244 | var_1 (ArrayVariable): The array variable to enforce distinctness within a slice. 245 | size (int): The number of elements in the slice to ensure distinctness for. 246 | offset_start_x (int): The starting offset along the x-axis for the slice. 247 | offset_start_y (int): The starting offset along the y-axis for the slice. 248 | offset_end_x (int): The ending offset along the x-axis for the slice. 249 | offset_end_y (int): The ending offset along the y-axis for the slice. 250 | constraint_name (str, optional): A name for the constraint. 251 | """ 252 | super().__init__(constraint_name) 253 | self.var_1 = var_1 254 | self.size = size 255 | self.offset_start_x = offset_start_x 256 | self.offset_start_y = offset_start_y 257 | self.offset_end_x = offset_end_x 258 | self.offset_end_y = offset_end_y 259 | 260 | def to_json(self): 261 | """ 262 | Convert the constraint to a JSON representation. 263 | 264 | Returns: 265 | dict: A dictionary containing constraint information in JSON format. 266 | """ 267 | return { 268 | "name": self.constraint_name, 269 | "type": "distinct", 270 | "v1": self.var_1.var_name, 271 | "selection": "slice", 272 | "size": self.size, 273 | "offset_start_x": self.offset_start_x, 274 | "offset_start_y": self.offset_start_y, 275 | "offset_end_x": self.offset_end_x, 276 | "offset_end_y": self.offset_end_y, 277 | } 278 | -------------------------------------------------------------------------------- /qaekwy/model/constraint/divide.py: -------------------------------------------------------------------------------- 1 | """ConstraintDivide Module 2 | 3 | This module defines the ConstraintDivide class, which represents a 4 | constraint to enforce a division relationship between three variables. 5 | 6 | Classes: 7 | ConstraintDivide: Represents a constraint to enforce a division 8 | relationship between three variables. 9 | 10 | """ 11 | 12 | from qaekwy.model.constraint.abstract_constraint import AbstractConstraint 13 | from qaekwy.model.variable.variable import Variable 14 | 15 | 16 | class ConstraintDivide(AbstractConstraint): 17 | """ 18 | Represents a constraint to enforce a division relationship between three variables. 19 | 20 | This constraint enforces that the division of var_1 by var_2 is equal to var_3. 21 | 22 | Args: 23 | var_1 (Variable): The numerator variable in the division. 24 | var_2 (Variable): The denominator variable in the division. 25 | var_3 (Variable): The result variable of the division. 26 | constraint_name (str, optional): A name for the constraint. 27 | 28 | Attributes: 29 | var_1 (Variable): The numerator variable in the division. 30 | var_2 (Variable): The denominator variable in the division. 31 | var_3 (Variable): The result variable of the division. 32 | 33 | Methods: 34 | to_json(): Returns a JSON representation of the constraint. 35 | 36 | Example: 37 | divide_constraint = 38 | ConstraintDivide(numerator, denominator, result, "divide_constraint") 39 | """ 40 | 41 | def __init__( 42 | self, var_1: Variable, var_2: Variable, var_3: Variable, constraint_name=None 43 | ) -> None: 44 | """ 45 | Initialize a new division constraint instance. 46 | 47 | Args: 48 | var_1 (Variable): The numerator variable in the division. 49 | var_2 (Variable): The denominator variable in the division. 50 | var_3 (Variable): The result variable of the division. 51 | constraint_name (str, optional): A name for the constraint. 52 | """ 53 | super().__init__(constraint_name) 54 | self.var_1 = var_1 55 | self.var_2 = var_2 56 | self.var_3 = var_3 57 | 58 | def to_json(self) -> dict: 59 | """ 60 | Convert the constraint to a JSON representation. 61 | 62 | Returns: 63 | dict: A dictionary containing constraint information in JSON format. 64 | """ 65 | return { 66 | "name": self.constraint_name, 67 | "v1": self.var_1.var_name, 68 | "v2": self.var_2.var_name, 69 | "v3": self.var_3.var_name, 70 | "type": "div", 71 | } 72 | -------------------------------------------------------------------------------- /qaekwy/model/constraint/element.py: -------------------------------------------------------------------------------- 1 | """ConstraintElement Module 2 | 3 | This module defines the ConstraintElement class, which represents 4 | a constraint to enforce an element-wise relationship between two 5 | variables based on a mapping array. 6 | 7 | Classes: 8 | ConstraintElement: Represents a constraint to enforce an element-wise 9 | relationship between two variables. 10 | 11 | """ 12 | 13 | from qaekwy.model.constraint.abstract_constraint import AbstractConstraint 14 | from qaekwy.model.variable.variable import ArrayVariable, Variable 15 | 16 | 17 | class ConstraintElement(AbstractConstraint): 18 | """ 19 | Represents a constraint to enforce an element-wise relationship 20 | between two variables. 21 | 22 | This constraint enforces that the values of var_1 and var_2 are 23 | related element-wise based on a mapping array. 24 | 25 | Args: 26 | map_array (ArrayVariable): The mapping array that defines the element-wise relationship. 27 | var_1 (Variable): The first variable in the relationship. 28 | var_2 (Variable): The second variable in the relationship. 29 | constraint_name (str, optional): A name for the constraint. 30 | 31 | Attributes: 32 | map_array (ArrayVariable): The mapping array that defines the element-wise relationship. 33 | var_1 (Variable): The first variable in the relationship. 34 | var_2 (Variable): The second variable in the relationship. 35 | 36 | Methods: 37 | to_json(): Returns a JSON representation of the constraint. 38 | 39 | Example: 40 | element_constraint = 41 | ConstraintElement(mapping_array, variable_1, variable_2, "element_constraint") 42 | """ 43 | 44 | def __init__( 45 | self, 46 | map_array: ArrayVariable, 47 | var_1: Variable, 48 | var_2: Variable, 49 | constraint_name=None, 50 | ) -> None: 51 | """ 52 | Initialize a new element-wise constraint instance. 53 | 54 | Args: 55 | map_array (ArrayVariable): The mapping array that defines the element-wise relationship. 56 | var_1 (Variable): The first variable in the relationship. 57 | var_2 (Variable): The second variable in the relationship. 58 | constraint_name (str, optional): A name for the constraint. 59 | """ 60 | super().__init__(constraint_name) 61 | self.map_array = map_array 62 | self.var_1 = var_1 63 | self.var_2 = var_2 64 | 65 | def to_json(self): 66 | """ 67 | Convert the constraint to a JSON representation. 68 | 69 | Returns: 70 | dict: A dictionary containing constraint information in JSON format. 71 | """ 72 | return { 73 | "name": self.constraint_name, 74 | "map": self.map_array.var_name, 75 | "v1": self.var_1.var_name, 76 | "v2": self.var_2.var_name, 77 | "type": "element", 78 | } 79 | -------------------------------------------------------------------------------- /qaekwy/model/constraint/exponential.py: -------------------------------------------------------------------------------- 1 | """ConstraintExponential Module 2 | 3 | This module defines the ConstraintExponential class, which represents 4 | a constraint to enforce an exponential relationship between two variables. 5 | 6 | Classes: 7 | ConstraintExponential: Represents a constraint to enforce an 8 | exponential relationship between two variables. 9 | 10 | """ 11 | 12 | from qaekwy.model.constraint.abstract_constraint import AbstractConstraint 13 | from qaekwy.model.variable.variable import Variable 14 | 15 | 16 | class ConstraintExponential(AbstractConstraint): 17 | """ 18 | Represents a constraint to enforce an exponential relationship between two variables. 19 | 20 | This constraint enforces that the exponential of var_1 is equal to var_2. 21 | 22 | Args: 23 | var_1 (Variable): The base variable in the exponential relationship. 24 | var_2 (Variable): The result variable of the exponential relationship. 25 | constraint_name (str, optional): A name for the constraint. 26 | 27 | Attributes: 28 | var_1 (Variable): The base variable in the exponential relationship. 29 | var_2 (Variable): The result variable of the exponential relationship. 30 | 31 | Methods: 32 | to_json(): Returns a JSON representation of the constraint. 33 | 34 | Example: 35 | exponential_constraint = 36 | ConstraintExponential(base_variable, result_variable, "exponential_constraint") 37 | """ 38 | 39 | def __init__(self, var_1: Variable, var_2: Variable, constraint_name=None) -> None: 40 | """ 41 | Initialize a new exponential constraint instance. 42 | 43 | Args: 44 | var_1 (Variable): The base variable in the exponential relationship. 45 | var_2 (Variable): The result variable of the exponential relationship. 46 | constraint_name (str, optional): A name for the constraint. 47 | """ 48 | super().__init__(constraint_name) 49 | self.var_1 = var_1 50 | self.var_2 = var_2 51 | 52 | def to_json(self) -> dict: 53 | """ 54 | Convert the constraint to a JSON representation. 55 | 56 | Returns: 57 | dict: A dictionary containing constraint information in JSON format. 58 | """ 59 | return { 60 | "name": self.constraint_name, 61 | "v1": self.var_1.var_name, 62 | "v2": self.var_2.var_name, 63 | "type": "div", 64 | } 65 | -------------------------------------------------------------------------------- /qaekwy/model/constraint/logarithm.py: -------------------------------------------------------------------------------- 1 | """ConstraintLogarithme Module 2 | 3 | This module defines the ConstraintLogarithme class, which represents a constraint to enforce 4 | a logarithmic relationship between two variables. 5 | 6 | Classes: 7 | ConstraintLogarithme: Represents a constraint to enforce a logarithmic 8 | relationship between two variables. 9 | 10 | """ 11 | 12 | from qaekwy.model.constraint.abstract_constraint import AbstractConstraint 13 | from qaekwy.model.variable.variable import Variable 14 | 15 | 16 | class ConstraintLogarithme(AbstractConstraint): 17 | """ 18 | Represents a constraint to enforce a logarithmic relationship between two variables. 19 | 20 | This constraint enforces that the logarithm of var_1 is equal to var_2, or vice versa. 21 | 22 | Args: 23 | var_1 (Variable): The variable for which the logarithm is enforced. 24 | var_2 (Variable): The result variable of the logarithmic relationship. 25 | constraint_name (str, optional): A name for the constraint. 26 | 27 | Attributes: 28 | var_1 (Variable): The variable for which the logarithm is enforced. 29 | var_2 (Variable): The result variable of the logarithmic relationship. 30 | 31 | Methods: 32 | to_json(): Returns a JSON representation of the constraint. 33 | 34 | Example: 35 | logarithmic_constraint = 36 | ConstraintLogarithme(variable_to_log, result_variable, "logarithmic_constraint") 37 | """ 38 | 39 | def __init__(self, var_1: Variable, var_2: Variable, constraint_name=None) -> None: 40 | """ 41 | Initialize a new logarithmic constraint instance. 42 | 43 | Args: 44 | var_1 (Variable): The variable for which the logarithm is enforced. 45 | var_2 (Variable): The result variable of the logarithmic relationship. 46 | constraint_name (str, optional): A name for the constraint. 47 | """ 48 | super().__init__(constraint_name) 49 | self.var_1 = var_1 50 | self.var_2 = var_2 51 | 52 | def to_json(self) -> dict: 53 | """ 54 | Convert the constraint to a JSON representation. 55 | 56 | Returns: 57 | dict: A dictionary containing constraint information in JSON format. 58 | """ 59 | return { 60 | "name": self.constraint_name, 61 | "v1": self.var_1.var_name, 62 | "v2": self.var_2.var_name, 63 | "type": "log", 64 | } 65 | -------------------------------------------------------------------------------- /qaekwy/model/constraint/maximum.py: -------------------------------------------------------------------------------- 1 | """ConstraintMaximum Module 2 | 3 | This module defines the ConstraintMaximum class, which represents a 4 | constraint to enforce a maximum value relationship between three variables. 5 | 6 | Classes: 7 | ConstraintMaximum: Represents a constraint to enforce a maximum 8 | value relationship between three variables. 9 | 10 | """ 11 | 12 | from qaekwy.model.constraint.abstract_constraint import AbstractConstraint 13 | from qaekwy.model.variable.variable import Variable 14 | 15 | 16 | class ConstraintMaximum(AbstractConstraint): 17 | """ 18 | Represents a constraint to enforce a maximum value relationship between two variables. 19 | 20 | This constraint enforces max{var_1, var_2} == var_3 21 | 22 | Args: 23 | var_1 (Variable): The first variable in the maximum value relationship. 24 | var_2 (Variable): The second variable in the maximum value relationship. 25 | var_3 (Variable): The third variable in the maximum value relationship. 26 | constraint_name (str, optional): A name for the constraint. 27 | 28 | Attributes: 29 | var_1 (Variable): The first variable in the maximum value relationship. 30 | var_2 (Variable): The second variable in the maximum value relationship. 31 | var_3 (Variable): The third variable in the maximum value relationship. 32 | 33 | Methods: 34 | to_json(): Returns a JSON representation of the constraint. 35 | 36 | Example: 37 | max_constraint = ConstraintMaximum(variable_1, variable_2, variable_3, "max_constraint") 38 | """ 39 | 40 | def __init__( 41 | self, var_1: Variable, var_2: Variable, var_3: Variable, constraint_name=None 42 | ) -> None: 43 | """ 44 | Initialize a new maximum value constraint instance. 45 | 46 | Args: 47 | var_1 (Variable): The first variable in the maximum value relationship. 48 | var_2 (Variable): The second variable in the maximum value relationship. 49 | var_3 (Variable): The third variable in the maximum value relationship. 50 | constraint_name (str, optional): A name for the constraint. 51 | """ 52 | super().__init__(constraint_name) 53 | self.var_1 = var_1 54 | self.var_2 = var_2 55 | self.var_3 = var_3 56 | 57 | def to_json(self) -> dict: 58 | """ 59 | Convert the constraint to a JSON representation. 60 | 61 | Returns: 62 | dict: A dictionary containing constraint information in JSON format. 63 | """ 64 | return { 65 | "name": self.constraint_name, 66 | "v1": self.var_1.var_name, 67 | "v2": self.var_2.var_name, 68 | "v3": self.var_3.var_name, 69 | "type": "max", 70 | } 71 | -------------------------------------------------------------------------------- /qaekwy/model/constraint/member.py: -------------------------------------------------------------------------------- 1 | """ConstraintMember Module 2 | 3 | This module defines the ConstraintMember class, which represents a 4 | constraint to enforce a membership relationship between an array 5 | variable and a variable. 6 | 7 | Classes: 8 | ConstraintMember: Represents a constraint to enforce a membership 9 | relationship between an array variable and a variable. 10 | 11 | """ 12 | 13 | from qaekwy.model.constraint.abstract_constraint import AbstractConstraint 14 | from qaekwy.model.variable.variable import ArrayVariable, Variable 15 | 16 | 17 | class ConstraintMember(AbstractConstraint): 18 | """ 19 | Represents a constraint to enforce a membership relationship between an 20 | array variable and a variable. 21 | 22 | This constraint enforces that the value of var_2 is a member of the array var_1. 23 | 24 | Args: 25 | var_1 (ArrayVariable): The array variable in the membership relationship. 26 | var_2 (Variable): The variable to be checked for membership. 27 | constraint_name (str, optional): A name for the constraint. 28 | 29 | Attributes: 30 | var_1 (ArrayVariable): The array variable in the membership relationship. 31 | var_2 (Variable): The variable to be checked for membership. 32 | 33 | Methods: 34 | to_json(): Returns a JSON representation of the constraint. 35 | 36 | Example: 37 | member_constraint = 38 | ConstraintMember(array_variable, variable_to_check, "member_constraint") 39 | """ 40 | 41 | def __init__( 42 | self, var_1: ArrayVariable, var_2: Variable, constraint_name=None 43 | ) -> None: 44 | """ 45 | Initialize a new membership constraint instance. 46 | 47 | Args: 48 | var_1 (ArrayVariable): The array variable in the membership relationship. 49 | var_2 (Variable): The variable to be checked for membership. 50 | constraint_name (str, optional): A name for the constraint. 51 | """ 52 | super().__init__(constraint_name) 53 | self.var_1 = var_1 54 | self.var_2 = var_2 55 | 56 | def to_json(self): 57 | """ 58 | Convert the constraint to a JSON representation. 59 | 60 | Returns: 61 | dict: A dictionary containing constraint information in JSON format. 62 | """ 63 | return { 64 | "name": self.constraint_name, 65 | "v1": self.var_1.var_name, 66 | "v2": self.var_2.var_name, 67 | "type": "member", 68 | } 69 | -------------------------------------------------------------------------------- /qaekwy/model/constraint/minimum.py: -------------------------------------------------------------------------------- 1 | """ConstraintMinimum Module 2 | 3 | This module defines the ConstraintMinimum class, which represents a 4 | constraint to enforce a minimum value relationship between three variables. 5 | 6 | Classes: 7 | ConstraintMinimum: Represents a constraint to enforce a minimum 8 | value relationship between three variables. 9 | 10 | """ 11 | 12 | from qaekwy.model.constraint.abstract_constraint import AbstractConstraint 13 | from qaekwy.model.variable.variable import Variable 14 | 15 | 16 | class ConstraintMinimum(AbstractConstraint): 17 | """ 18 | Represents a constraint to enforce a minimum value relationship between two variables. 19 | 20 | This constraint enforces min{var_1, var_2} == var_3 21 | 22 | Args: 23 | var_1 (Variable): The first variable in the minimum value relationship. 24 | var_2 (Variable): The second variable in the minimum value relationship. 25 | var_3 (Variable): The third variable in the minimum value relationship. 26 | constraint_name (str, optional): A name for the constraint. 27 | 28 | Attributes: 29 | var_1 (Variable): The first variable in the minimum value relationship. 30 | var_2 (Variable): The second variable in the minimum value relationship. 31 | var_3 (Variable): The third variable in the minimum value relationship. 32 | 33 | Methods: 34 | to_json(): Returns a JSON representation of the constraint. 35 | 36 | Example: 37 | min_constraint = ConstraintMinimum(variable_1, variable_2, variable_3, "min_constraint") 38 | """ 39 | 40 | def __init__( 41 | self, var_1: Variable, var_2: Variable, var_3: Variable, constraint_name=None 42 | ) -> None: 43 | """ 44 | Initialize a new minimum value constraint instance. 45 | 46 | Args: 47 | var_1 (Variable): The first variable in the minimum value relationship. 48 | var_2 (Variable): The second variable in the minimum value relationship. 49 | var_3 (Variable): The third variable in the minimum value relationship. 50 | constraint_name (str, optional): A name for the constraint. 51 | """ 52 | super().__init__(constraint_name) 53 | self.var_1 = var_1 54 | self.var_2 = var_2 55 | self.var_3 = var_3 56 | 57 | def to_json(self) -> dict: 58 | """ 59 | Convert the constraint to a JSON representation. 60 | 61 | Returns: 62 | dict: A dictionary containing constraint information in JSON format. 63 | """ 64 | return { 65 | "name": self.constraint_name, 66 | "v1": self.var_1.var_name, 67 | "v2": self.var_2.var_name, 68 | "v3": self.var_3.var_name, 69 | "type": "min", 70 | } 71 | -------------------------------------------------------------------------------- /qaekwy/model/constraint/modulo.py: -------------------------------------------------------------------------------- 1 | """ConstraintModulo Module 2 | 3 | This module defines the ConstraintModulo class, which represents a 4 | constraint to enforce a modulo relationship between three variables. 5 | 6 | Classes: 7 | ConstraintModulo: Represents a constraint to enforce a modulo 8 | relationship between three variables. 9 | 10 | """ 11 | 12 | from qaekwy.model.constraint.abstract_constraint import AbstractConstraint 13 | from qaekwy.model.variable.variable import Variable 14 | 15 | 16 | class ConstraintModulo(AbstractConstraint): 17 | """ 18 | Represents a constraint to enforce a modulo relationship between three variables. 19 | 20 | This constraint enforces that the remainder of dividing var_1 by var_2 is equal to var_3. 21 | 22 | Args: 23 | var_1 (Variable): The dividend variable in the modulo relationship. 24 | var_2 (Variable): The divisor variable in the modulo relationship. 25 | var_3 (Variable): The result variable of the modulo relationship. 26 | constraint_name (str, optional): A name for the constraint. 27 | 28 | Attributes: 29 | var_1 (Variable): The dividend variable in the modulo relationship. 30 | var_2 (Variable): The divisor variable in the modulo relationship. 31 | var_3 (Variable): The result variable of the modulo relationship. 32 | 33 | Methods: 34 | to_json(): Returns a JSON representation of the constraint. 35 | 36 | Example: 37 | modulo_constraint = 38 | ConstraintModulo( 39 | dividend_variable, 40 | divisor_variable, 41 | result_variable, 42 | "modulo_constraint" 43 | ) 44 | """ 45 | 46 | def __init__( 47 | self, var_1: Variable, var_2: Variable, var_3: Variable, constraint_name=None 48 | ) -> None: 49 | """ 50 | Initialize a new modulo constraint instance. 51 | 52 | Args: 53 | var_1 (Variable): The dividend variable in the modulo relationship. 54 | var_2 (Variable): The divisor variable in the modulo relationship. 55 | var_3 (Variable): The result variable of the modulo relationship. 56 | constraint_name (str, optional): A name for the constraint. 57 | """ 58 | super().__init__(constraint_name) 59 | self.var_1 = var_1 60 | self.var_2 = var_2 61 | self.var_3 = var_3 62 | 63 | def to_json(self) -> dict: 64 | """ 65 | Convert the constraint to a JSON representation. 66 | 67 | Returns: 68 | dict: A dictionary containing constraint information in JSON format. 69 | """ 70 | return { 71 | "name": self.constraint_name, 72 | "v1": self.var_1.var_name, 73 | "v2": self.var_2.var_name, 74 | "v3": self.var_3.var_name, 75 | "type": "mod", 76 | } 77 | -------------------------------------------------------------------------------- /qaekwy/model/constraint/multiply.py: -------------------------------------------------------------------------------- 1 | """ConstraintMultiply Module 2 | 3 | This module defines the ConstraintMultiply class, which represents a 4 | constraint to enforce a multiplication relationship between three 5 | variables. 6 | 7 | Classes: 8 | ConstraintMultiply: Represents a constraint to enforce a 9 | multiplication relationship between three variables. 10 | 11 | """ 12 | 13 | from qaekwy.model.constraint.abstract_constraint import AbstractConstraint 14 | from qaekwy.model.variable.variable import Variable 15 | 16 | 17 | class ConstraintMultiply(AbstractConstraint): 18 | """ 19 | Represents a constraint to enforce a multiplication relationship between three variables. 20 | 21 | This constraint enforces that the product of var_1 and var_2 is equal to var_3. 22 | 23 | Args: 24 | var_1 (Variable): The first variable in the multiplication relationship. 25 | var_2 (Variable): The second variable in the multiplication relationship. 26 | var_3 (Variable): The result variable of the multiplication relationship. 27 | constraint_name (str, optional): A name for the constraint. 28 | 29 | Attributes: 30 | var_1 (Variable): The first variable in the multiplication relationship. 31 | var_2 (Variable): The second variable in the multiplication relationship. 32 | var_3 (Variable): The result variable of the multiplication relationship. 33 | 34 | Methods: 35 | to_json(): Returns a JSON representation of the constraint. 36 | 37 | Example: 38 | multiply_constraint = 39 | ConstraintMultiply(variable_1, variable_2, result_variable, "multiply_constraint") 40 | """ 41 | 42 | def __init__( 43 | self, var_1: Variable, var_2: Variable, var_3: Variable, constraint_name=None 44 | ) -> None: 45 | """ 46 | Initialize a new multiplication constraint instance. 47 | 48 | Args: 49 | var_1 (Variable): The first variable in the multiplication relationship. 50 | var_2 (Variable): The second variable in the multiplication relationship. 51 | var_3 (Variable): The result variable of the multiplication relationship. 52 | constraint_name (str, optional): A name for the constraint. 53 | """ 54 | super().__init__(constraint_name) 55 | self.var_1 = var_1 56 | self.var_2 = var_2 57 | self.var_3 = var_3 58 | 59 | def to_json(self) -> dict: 60 | """ 61 | Convert the constraint to a JSON representation. 62 | 63 | Returns: 64 | dict: A dictionary containing constraint information in JSON format. 65 | """ 66 | return { 67 | "name": self.constraint_name, 68 | "v1": self.var_1.var_name, 69 | "v2": self.var_2.var_name, 70 | "v3": self.var_3.var_name, 71 | "type": "mul", 72 | } 73 | -------------------------------------------------------------------------------- /qaekwy/model/constraint/nroot.py: -------------------------------------------------------------------------------- 1 | """ConstraintNRoot Module 2 | 3 | This module defines the ConstraintNRoot class, which represents a 4 | constraint to enforce an n-th root relationship between three variables. 5 | 6 | Classes: 7 | ConstraintNRoot: Represents a constraint to enforce an n-th root 8 | relationship between three variables. 9 | 10 | """ 11 | 12 | from qaekwy.model.constraint.abstract_constraint import AbstractConstraint 13 | from qaekwy.model.variable.variable import Variable 14 | 15 | 16 | class ConstraintNRoot(AbstractConstraint): 17 | """ 18 | Represents a constraint to enforce an n-th root relationship between three variables. 19 | 20 | This constraint enforces that the n-th root of var_1 is equal to var_3, or vice versa. 21 | 22 | Args: 23 | var_1 (Variable): The variable for which the n-th root is enforced. 24 | var_2 (int): The value of n for the n-th root. 25 | var_3 (Variable): The result variable of the n-th root relationship. 26 | constraint_name (str, optional): A name for the constraint. 27 | 28 | Attributes: 29 | var_1 (Variable): The variable for which the n-th root is enforced. 30 | var_2 (int): The value of n for the n-th root. 31 | var_3 (Variable): The result variable of the n-th root relationship. 32 | 33 | Methods: 34 | to_json(): Returns a JSON representation of the constraint. 35 | 36 | Example: 37 | nroot_constraint = 38 | ConstraintNRoot(variable_to_root, n_value, result_variable, "nroot_constraint") 39 | """ 40 | 41 | def __init__( 42 | self, var_1: Variable, var_2: int, var_3: Variable, constraint_name=None 43 | ) -> None: 44 | """ 45 | Initialize a new n-th root constraint instance. 46 | 47 | Args: 48 | var_1 (Variable): The variable for which the n-th root is enforced. 49 | var_2 (int): The value of n for the n-th root. 50 | var_3 (Variable): The result variable of the n-th root relationship. 51 | constraint_name (str, optional): A name for the constraint. 52 | """ 53 | super().__init__(constraint_name) 54 | self.var_1 = var_1 55 | self.var_2 = var_2 56 | self.var_3 = var_3 57 | 58 | def to_json(self) -> dict: 59 | """ 60 | Convert the constraint to a JSON representation. 61 | 62 | Returns: 63 | dict: A dictionary containing constraint information in JSON format. 64 | """ 65 | return { 66 | "name": self.constraint_name, 67 | "v1": self.var_1.var_name, 68 | "v2": self.var_2, 69 | "v3": self.var_3.var_name, 70 | "type": "nroot", 71 | } 72 | -------------------------------------------------------------------------------- /qaekwy/model/constraint/power.py: -------------------------------------------------------------------------------- 1 | """ConstraintPower Module 2 | 3 | This module defines the ConstraintPower class, which represents a 4 | constraint to enforce a power relationship between three variables. 5 | 6 | Classes: 7 | ConstraintPower: Represents a constraint to enforce a 8 | power relationship between three variables. 9 | 10 | """ 11 | 12 | from qaekwy.model.constraint.abstract_constraint import AbstractConstraint 13 | from qaekwy.model.variable.variable import Variable 14 | 15 | 16 | class ConstraintPower(AbstractConstraint): 17 | """ 18 | Represents a constraint to enforce a power relationship between three variables. 19 | 20 | This constraint enforces that the value of var_1 raised to the power of var_2 is equal to var_3. 21 | 22 | Args: 23 | var_1 (Variable): The base variable in the power relationship. 24 | var_2 (int): The exponent variable in the power relationship. 25 | var_3 (Variable): The result variable of the power relationship. 26 | constraint_name (str, optional): A name for the constraint. 27 | 28 | Attributes: 29 | var_1 (Variable): The base variable in the power relationship. 30 | var_2 (int): The exponent variable in the power relationship. 31 | var_3 (Variable): The result variable of the power relationship. 32 | 33 | Methods: 34 | to_json(): Returns a JSON representation of the constraint. 35 | 36 | Example: 37 | power_constraint = 38 | ConstraintPower(base_variable, exponent_value, result_variable, "power_constraint") 39 | """ 40 | 41 | def __init__( 42 | self, var_1: Variable, var_2: int, var_3: Variable, constraint_name=None 43 | ) -> None: 44 | """ 45 | Initialize a new power constraint instance. 46 | 47 | Args: 48 | var_1 (Variable): The base variable in the power relationship. 49 | var_2 (int): The exponent variable in the power relationship. 50 | var_3 (Variable): The result variable of the power relationship. 51 | constraint_name (str, optional): A name for the constraint. 52 | """ 53 | super().__init__(constraint_name) 54 | self.var_1 = var_1 55 | self.var_2 = var_2 56 | self.var_3 = var_3 57 | 58 | def to_json(self) -> dict: 59 | """ 60 | Convert the constraint to a JSON representation. 61 | 62 | Returns: 63 | dict: A dictionary containing constraint information in JSON format. 64 | """ 65 | return { 66 | "name": self.constraint_name, 67 | "v1": self.var_1.var_name, 68 | "v2": self.var_2, 69 | "v3": self.var_3.var_name, 70 | "type": "pow", 71 | } 72 | -------------------------------------------------------------------------------- /qaekwy/model/constraint/relational.py: -------------------------------------------------------------------------------- 1 | """RelationalExpression Module 2 | 3 | This module defines the RelationalExpression class, which represents a 4 | constraint using a relational expression between variables and values. 5 | 6 | Classes: 7 | RelationalExpression: Represents a constraint using a relational 8 | expression between variables or values. 9 | 10 | """ 11 | from qaekwy.model.constraint.abstract_constraint import AbstractConstraint 12 | from qaekwy.model.variable.variable import Expression 13 | 14 | 15 | class RelationalExpression(AbstractConstraint): 16 | """ 17 | Represents a constraint using a relational expression between variables or values. 18 | 19 | This constraint enforces a relational expression that can be evaluated as True or False. 20 | The expression can involve variables, constants, and mathematical operators. 21 | 22 | Args: 23 | expr (Expression): The relational expression to be enforced. 24 | constraint_name (str, optional): A name for the constraint. 25 | 26 | Attributes: 27 | expr (Expression): The relational expression to be enforced. 28 | 29 | Methods: 30 | to_json(): Returns a JSON representation of the constraint. 31 | 32 | Example: 33 | expression = Expression(var_1 + var_2 >= var_3 + 1) 34 | relational_constraint = 35 | RelationalExpression(expression, "relational_constraint") 36 | """ 37 | 38 | def __init__(self, expr: Expression, constraint_name=None) -> None: 39 | """ 40 | Initialize a new relational expression constraint instance. 41 | 42 | Args: 43 | expr (Expression): The relational expression to be enforced. 44 | constraint_name (str, optional): A name for the constraint. 45 | """ 46 | super().__init__(constraint_name) 47 | self.expr = expr 48 | 49 | def to_json(self) -> dict: 50 | """ 51 | Convert the constraint to a JSON representation. 52 | 53 | Returns: 54 | dict: A dictionary containing constraint information in JSON format. 55 | """ 56 | return {"name": self.constraint_name, "expr": str(self.expr), "type": "rel"} 57 | -------------------------------------------------------------------------------- /qaekwy/model/constraint/sin.py: -------------------------------------------------------------------------------- 1 | """ConstraintSin Module 2 | 3 | This module defines the ConstraintSin class, which represents a 4 | constraint to enforce a sine relationship between two variables. 5 | 6 | Classes: 7 | ConstraintSin: Represents a constraint to enforce a sine relationship between two variables. 8 | 9 | """ 10 | from qaekwy.model.constraint.abstract_constraint import AbstractConstraint 11 | from qaekwy.model.variable.variable import Variable 12 | 13 | 14 | class ConstraintSin(AbstractConstraint): 15 | """ 16 | Represents a constraint to enforce a sine relationship between two variables. 17 | 18 | This constraint enforces that the sine of var_1 is equal to var_2, or vice versa. 19 | 20 | Args: 21 | var_1 (Variable): The variable for which the sine relationship is enforced. 22 | var_2 (Variable): The result variable of the sine relationship. 23 | constraint_name (str, optional): A name for the constraint. 24 | 25 | Attributes: 26 | var_1 (Variable): The variable for which the sine relationship is enforced. 27 | var_2 (Variable): The result variable of the sine relationship. 28 | 29 | Methods: 30 | to_json(): Returns a JSON representation of the constraint. 31 | 32 | Example: 33 | sine_constraint = 34 | ConstraintSin(variable_to_sine, result_variable, "sine_constraint") 35 | """ 36 | 37 | def __init__(self, var_1: Variable, var_2: Variable, constraint_name=None) -> None: 38 | """ 39 | Initialize a new sine constraint instance. 40 | 41 | Args: 42 | var_1 (Variable): The variable for which the sine relationship is enforced. 43 | var_2 (Variable): The result variable of the sine relationship. 44 | constraint_name (str, optional): A name for the constraint. 45 | """ 46 | super().__init__(constraint_name) 47 | self.var_1 = var_1 48 | self.var_2 = var_2 49 | 50 | def to_json(self) -> dict: 51 | """ 52 | Convert the constraint to a JSON representation. 53 | 54 | Returns: 55 | dict: A dictionary containing constraint information in JSON format. 56 | """ 57 | return { 58 | "name": self.constraint_name, 59 | "v1": self.var_1.var_name, 60 | "v2": self.var_2.var_name, 61 | "type": "sin", 62 | } 63 | -------------------------------------------------------------------------------- /qaekwy/model/constraint/sort.py: -------------------------------------------------------------------------------- 1 | """ConstraintSorted and ConstraintReverseSorted Module 2 | 3 | This module defines the ConstraintSorted and ConstraintReverseSorted classes, 4 | which represent constraints to enforce sorted and reverse-sorted relationships 5 | among elements of an array variable. 6 | 7 | Classes: 8 | ConstraintSorted: Represents a constraint to enforce a sorted relationship 9 | among elements of an array variable. 10 | 11 | ConstraintReverseSorted: Represents a constraint to enforce a reverse-sorted 12 | relationship among elements of an array variable. 13 | 14 | """ 15 | 16 | from qaekwy.model.constraint.abstract_constraint import AbstractConstraint 17 | from qaekwy.model.variable.variable import ArrayVariable 18 | 19 | 20 | class ConstraintSorted(AbstractConstraint): 21 | """ 22 | Represents a constraint to enforce a sorted relationship among elements 23 | of an array variable. 24 | 25 | This constraint enforces that the elements of the array variable var_1 26 | are in ascending sorted order. 27 | 28 | Args: 29 | var_1 (ArrayVariable): The array variable for which the sorted relationship is enforced. 30 | constraint_name (str, optional): A name for the constraint. 31 | 32 | Attributes: 33 | var_1 (ArrayVariable): The array variable for which the sorted relationship is enforced. 34 | 35 | Methods: 36 | to_json(): Returns a JSON representation of the constraint. 37 | 38 | Example: 39 | array_to_sort = ArrayVariable("array_to_sort") 40 | sorted_constraint = ConstraintSorted(array_to_sort, "sorted_constraint") 41 | constraint_json = sorted_constraint.to_json() 42 | """ 43 | 44 | def __init__(self, var_1: ArrayVariable, constraint_name=None) -> None: 45 | """ 46 | Initialize a new sorted constraint instance. 47 | 48 | Args: 49 | var_1 (ArrayVariable): The array variable for which the sorted 50 | relationship is enforced. 51 | constraint_name (str, optional): A name for the constraint. 52 | """ 53 | super().__init__(constraint_name) 54 | self.var_1 = var_1 55 | 56 | def to_json(self): 57 | """ 58 | Convert the constraint to a JSON representation. 59 | 60 | Returns: 61 | dict: A dictionary containing constraint information in JSON format. 62 | """ 63 | return { 64 | "name": self.constraint_name, 65 | "v1": self.var_1.var_name, 66 | "type": "sorted", 67 | } 68 | 69 | 70 | class ConstraintReverseSorted(AbstractConstraint): 71 | """ 72 | Represents a constraint to enforce a reverse-sorted relationship 73 | among elements of an array variable. 74 | 75 | This constraint enforces that the elements of the array variable var_1 76 | are in descending sorted order. 77 | 78 | Args: 79 | var_1 (ArrayVariable): The array variable for which the reverse-sorted 80 | relationship is enforced. 81 | constraint_name (str, optional): A name for the constraint. 82 | 83 | Attributes: 84 | var_1 (ArrayVariable): The array variable for which the reverse-sorted 85 | relationship is enforced. 86 | 87 | Methods: 88 | to_json(): Returns a JSON representation of the constraint. 89 | 90 | Example: 91 | reverse_sorted_constraint = 92 | ConstraintReverseSorted(array_to_reverse_sort, "reverse_sorted_constraint") 93 | """ 94 | 95 | def __init__(self, var_1: ArrayVariable, constraint_name=None) -> None: 96 | """ 97 | Initialize a new reverse-sorted constraint instance. 98 | 99 | Args: 100 | var_1 (ArrayVariable): The array variable for which the reverse-sorted 101 | relationship is enforced. 102 | constraint_name (str, optional): A name for the constraint. 103 | """ 104 | super().__init__(constraint_name) 105 | self.var_1 = var_1 106 | 107 | def to_json(self): 108 | """ 109 | Convert the constraint to a JSON representation. 110 | 111 | Returns: 112 | dict: A dictionary containing constraint information in JSON format. 113 | """ 114 | return { 115 | "name": self.constraint_name, 116 | "v1": self.var_1.var_name, 117 | "type": "rsorted", 118 | } 119 | -------------------------------------------------------------------------------- /qaekwy/model/constraint/tan.py: -------------------------------------------------------------------------------- 1 | """ConstraintTan Module 2 | 3 | This module defines the ConstraintTan class, which represents a 4 | constraint to enforce a tangent relationship between two variables. 5 | 6 | Classes: 7 | ConstraintTan: Represents a constraint to enforce a tangent 8 | relationship between two variables. 9 | 10 | """ 11 | from qaekwy.model.constraint.abstract_constraint import AbstractConstraint 12 | from qaekwy.model.variable.variable import Variable 13 | 14 | 15 | class ConstraintTan(AbstractConstraint): 16 | """ 17 | Represents a constraint to enforce a tangent relationship between two variables. 18 | 19 | This constraint enforces that the tangent of var_1 is equal to var_2, or vice versa. 20 | 21 | Args: 22 | var_1 (Variable): The variable for which the tangent relationship is enforced. 23 | var_2 (Variable): The result variable of the tangent relationship. 24 | constraint_name (str, optional): A name for the constraint. 25 | 26 | Attributes: 27 | var_1 (Variable): The variable for which the tangent relationship is enforced. 28 | var_2 (Variable): The result variable of the tangent relationship. 29 | 30 | Methods: 31 | to_json(): Returns a JSON representation of the constraint. 32 | 33 | Example: 34 | tangent_constraint = 35 | ConstraintTan(variable_to_tangent, result_variable, "tangent_constraint") 36 | """ 37 | 38 | def __init__(self, var_1: Variable, var_2: Variable, constraint_name=None) -> None: 39 | """ 40 | Initialize a new tangent constraint instance. 41 | 42 | Args: 43 | var_1 (Variable): The variable for which the tangent relationship is enforced. 44 | var_2 (Variable): The result variable of the tangent relationship. 45 | constraint_name (str, optional): A name for the constraint. 46 | """ 47 | super().__init__(constraint_name) 48 | self.var_1 = var_1 49 | self.var_2 = var_2 50 | 51 | def to_json(self) -> dict: 52 | """ 53 | Convert the constraint to a JSON representation. 54 | 55 | Returns: 56 | dict: A dictionary containing constraint information in JSON format. 57 | """ 58 | return { 59 | "name": self.constraint_name, 60 | "v1": self.var_1.var_name, 61 | "v2": self.var_2.var_name, 62 | "type": "tan", 63 | } 64 | -------------------------------------------------------------------------------- /qaekwy/model/cutoff.py: -------------------------------------------------------------------------------- 1 | """ 2 | This module provides concrete implementations of the Cutoff abstract base class for 3 | specifying optimization cutoff conditions. Each concrete class represents a specific 4 | type of optimization cutoff condition, such as constant, Fibonacci, and geometric progression. 5 | 6 | Classes: 7 | CutoffConstant: Represents a constant optimization cutoff condition. 8 | CutoffFibonacci: Represents a Fibonacci optimization cutoff condition. 9 | CutoffGeometric: Represents a geometric progression optimization cutoff condition. 10 | CutoffLuby: Represents a Luby sequence optimization cutoff condition. 11 | CutoffLinear: Represents a linear optimization cutoff condition. 12 | CutoffRandom: Represents a random optimization cutoff condition. 13 | 14 | This module also provides various meta-cutoff classes that allow users to combine 15 | and manipulate different cutoff conditions. Meta-cutoffs are composite cutoff conditions that 16 | apply multiple cutoffs in a specific manner to determine whether a search should be terminated. 17 | 18 | Classes: 19 | - MetaCutoffAppender: Combines two cutoff conditions by appending the second cutoff to the 20 | first after a certain number of solutions. 21 | - MetaCutoffMerger: Merges two cutoff conditions into a single cutoff by applying 22 | both conditions. 23 | - MetaCutoffRepeater: Repeats a sub-cutoff condition a specified number of times. 24 | 25 | Usage: 26 | from qaekwy.model.cutoff.meta_cutoff import ( 27 | MetaCutoffAppender, 28 | MetaCutoffMerger, 29 | MetaCutoffRepeater 30 | ) 31 | 32 | # Example usage of MetaCutoffAppender 33 | first_cutoff = CutoffConstant(100) 34 | second_cutoff = CutoffFibonacci() 35 | appender = MetaCutoffAppender(first_cutoff, number_from_first=10, second_cutoff=second_cutoff) 36 | 37 | # Example usage of MetaCutoffMerger 38 | first_cutoff = CutoffConstant(100) 39 | second_cutoff = CutoffGeometric(base=1.5, scale=10) 40 | merger = MetaCutoffMerger(first_cutoff, second_cutoff) 41 | 42 | # Example usage of MetaCutoffRepeater 43 | sub_cutoff = CutoffLinear(scale=5) 44 | repeater = MetaCutoffRepeater(sub_cutoff, repeat=3) 45 | 46 | """ 47 | 48 | from abc import ABC, abstractmethod 49 | 50 | 51 | class Cutoff(ABC): # pylint: disable=too-few-public-methods 52 | """ 53 | An abstract base class representing an optimization cutoff condition. 54 | 55 | Methods: 56 | is_meta() -> bool: 57 | Check if the cutoff condition is a meta-cutoff. 58 | 59 | to_json(): 60 | Convert the cutoff condition to a JSON representation. 61 | 62 | """ 63 | 64 | @abstractmethod 65 | def is_meta(self) -> bool: 66 | """ 67 | Check if the cutoff condition is a meta-cutoff. 68 | 69 | Returns: 70 | bool: True if the cutoff is a meta-cutoff, False otherwise. 71 | 72 | """ 73 | 74 | @abstractmethod 75 | def to_json(self): 76 | """ 77 | Convert the cutoff condition to a JSON representation. 78 | 79 | Returns: 80 | dict: A JSON representation of the cutoff condition. 81 | 82 | """ 83 | 84 | 85 | class CutoffConstant(Cutoff): # pylint: disable=too-few-public-methods 86 | """ 87 | Represents a constant optimization cutoff condition. 88 | 89 | Methods: 90 | is_meta() -> bool: 91 | Check if the cutoff condition is a meta-cutoff. 92 | 93 | to_json(): 94 | Convert the cutoff condition to a JSON representation. 95 | 96 | """ 97 | 98 | def __init__(self, constant_value: int) -> None: 99 | super().__init__() 100 | self.constant_value = constant_value 101 | 102 | def is_meta(self) -> bool: 103 | """ 104 | Check if the cutoff condition is a meta-cutoff. 105 | 106 | Returns: 107 | bool: False, indicating that this is not a meta-cutoff. 108 | 109 | """ 110 | return False 111 | 112 | def to_json(self): 113 | """ 114 | Convert the cutoff condition to a JSON representation. 115 | 116 | Returns: 117 | dict: A JSON representation of the cutoff condition. 118 | 119 | """ 120 | return {"name": "constant", "value": self.constant_value} 121 | 122 | 123 | class CutoffFibonacci(Cutoff): # pylint: disable=too-few-public-methods 124 | """ 125 | Represents a Fibonacci optimization cutoff condition. 126 | 127 | Methods: 128 | is_meta() -> bool: 129 | Check if the cutoff condition is a meta-cutoff. 130 | 131 | to_json(): 132 | Convert the cutoff condition to a JSON representation. 133 | 134 | """ 135 | 136 | def is_meta(self) -> bool: 137 | """ 138 | Check if the cutoff condition is a meta-cutoff. 139 | 140 | Returns: 141 | bool: False, indicating that this is not a meta-cutoff. 142 | 143 | """ 144 | return False 145 | 146 | def to_json(self): 147 | """ 148 | Convert the cutoff condition to a JSON representation. 149 | 150 | Returns: 151 | dict: A JSON representation of the cutoff condition. 152 | 153 | """ 154 | return {"name": "fibonacci"} 155 | 156 | 157 | class CutoffGeometric(Cutoff): # pylint: disable=too-few-public-methods 158 | """ 159 | Represents a geometric progression optimization cutoff condition. 160 | 161 | Methods: 162 | is_meta() -> bool: 163 | Check if the cutoff condition is a meta-cutoff. 164 | 165 | to_json(): 166 | Convert the cutoff condition to a JSON representation. 167 | 168 | """ 169 | 170 | def __init__(self, base: float, scale: int) -> None: 171 | super().__init__() 172 | self.base = base 173 | self.scale = scale 174 | 175 | def is_meta(self) -> bool: 176 | """ 177 | Check if the cutoff condition is a meta-cutoff. 178 | 179 | Returns: 180 | bool: False, indicating that this is not a meta-cutoff. 181 | 182 | """ 183 | return False 184 | 185 | def to_json(self): 186 | """ 187 | Convert the cutoff condition to a JSON representation. 188 | 189 | Returns: 190 | dict: A JSON representation of the cutoff condition. 191 | 192 | """ 193 | return {"name": "geometric", "scale": self.scale, "base": self.base} 194 | 195 | 196 | class CutoffLuby(Cutoff): # pylint: disable=too-few-public-methods 197 | """ 198 | Represents a Luby sequence optimization cutoff condition. 199 | 200 | Methods: 201 | is_meta() -> bool: 202 | Check if the cutoff condition is a meta-cutoff. 203 | 204 | to_json(): 205 | Convert the cutoff condition to a JSON representation. 206 | 207 | """ 208 | 209 | def __init__(self, scale: int) -> None: 210 | """ 211 | Initialize the CutoffLuby instance. 212 | 213 | Args: 214 | scale (int): The scaling factor for the Luby sequence. 215 | 216 | Returns: 217 | None 218 | 219 | """ 220 | super().__init__() 221 | self.scale = scale 222 | 223 | def is_meta(self) -> bool: 224 | """ 225 | Check if the cutoff condition is a meta-cutoff. 226 | 227 | Returns: 228 | bool: False, indicating that this is not a meta-cutoff. 229 | 230 | """ 231 | return False 232 | 233 | def to_json(self): 234 | """ 235 | Convert the cutoff condition to a JSON representation. 236 | 237 | Returns: 238 | dict: A JSON representation of the cutoff condition. 239 | 240 | """ 241 | return {"name": "luby", "scale": self.scale} 242 | 243 | 244 | class CutoffLinear(Cutoff): # pylint: disable=too-few-public-methods 245 | """ 246 | Represents a linear optimization cutoff condition. 247 | 248 | Methods: 249 | is_meta() -> bool: 250 | Check if the cutoff condition is a meta-cutoff. 251 | 252 | to_json(): 253 | Convert the cutoff condition to a JSON representation. 254 | 255 | """ 256 | 257 | def __init__(self, scale: int) -> None: 258 | """ 259 | Initialize the CutoffLinear instance. 260 | 261 | Args: 262 | scale (int): The scaling factor for the linear cutoff. 263 | 264 | Returns: 265 | None 266 | 267 | """ 268 | super().__init__() 269 | self.scale = scale 270 | 271 | def is_meta(self) -> bool: 272 | """ 273 | Check if the cutoff condition is a meta-cutoff. 274 | 275 | Returns: 276 | bool: False, indicating that this is not a meta-cutoff. 277 | 278 | """ 279 | return False 280 | 281 | def to_json(self): 282 | """ 283 | Convert the cutoff condition to a JSON representation. 284 | 285 | Returns: 286 | dict: A JSON representation of the cutoff condition. 287 | 288 | """ 289 | return {"name": "linear", "scale": self.scale} 290 | 291 | 292 | class CutoffRandom(Cutoff): # pylint: disable=too-few-public-methods 293 | """ 294 | Represents a random optimization cutoff condition. 295 | 296 | Methods: 297 | is_meta() -> bool: 298 | Check if the cutoff condition is a meta-cutoff. 299 | 300 | to_json(): 301 | Convert the cutoff condition to a JSON representation. 302 | 303 | """ 304 | 305 | def __init__(self, seed: int, minimum: int, maximum: int, round_value: int) -> None: 306 | """ 307 | Initialize the CutoffRandom instance. 308 | 309 | Args: 310 | seed (int): The seed value for the random number generator. 311 | minimum (int): The minimum cutoff value. 312 | maximum (int): The maximum cutoff value. 313 | round_value (int): The value to round the cutoff to. 314 | 315 | Returns: 316 | None 317 | 318 | """ 319 | super().__init__() 320 | self.seed = seed 321 | self.minimum = minimum 322 | self.maximum = maximum 323 | self.round_value = round_value 324 | 325 | def is_meta(self) -> bool: 326 | """ 327 | Check if the cutoff condition is a meta-cutoff. 328 | 329 | Returns: 330 | bool: False, indicating that this is not a meta-cutoff. 331 | 332 | """ 333 | return False 334 | 335 | def to_json(self): 336 | """ 337 | Convert the cutoff condition to a JSON representation. 338 | 339 | Returns: 340 | dict: A JSON representation of the cutoff condition. 341 | 342 | """ 343 | return { 344 | "name": "random", 345 | "seed": self.seed, 346 | "min": self.minimum, 347 | "max": self.maximum, 348 | "round": self.round_value, 349 | } 350 | 351 | 352 | class MetaCutoffAppender(Cutoff): # pylint: disable=too-few-public-methods 353 | """ 354 | Represents a meta-cutoff that appends two different cutoff conditions. 355 | 356 | Methods: 357 | __init__(first_cutoff: Cutoff, number_from_first: int, second_cutoff: Cutoff) -> None: 358 | Initialize the MetaCutoffAppender instance. 359 | 360 | is_meta() -> bool: 361 | Check if the cutoff condition is a meta-cutoff. 362 | 363 | to_json(): 364 | Convert the cutoff condition to a JSON representation. 365 | 366 | """ 367 | 368 | def __init__( 369 | self, first_cutoff: Cutoff, number_from_first: int, second_cutoff: Cutoff 370 | ) -> None: 371 | """ 372 | Initialize the MetaCutoffAppender instance. 373 | 374 | Args: 375 | first_cutoff (Cutoff): The first cutoff condition. 376 | number_from_first (int): The number of solutions from the first cutoff to append. 377 | second_cutoff (Cutoff): The second cutoff condition. 378 | 379 | """ 380 | super().__init__() 381 | self.first_cutoff = first_cutoff 382 | self.number_from_first = number_from_first 383 | self.second_cutoff = second_cutoff 384 | 385 | def is_meta(self) -> bool: 386 | """ 387 | Check if the cutoff condition is a meta-cutoff. 388 | 389 | Returns: 390 | bool: True, indicating that this is a meta-cutoff. 391 | 392 | """ 393 | return True 394 | 395 | def to_json(self): 396 | """ 397 | Convert the cutoff condition to a JSON representation. 398 | 399 | Returns: 400 | dict: A JSON representation of the cutoff condition. 401 | 402 | """ 403 | return { 404 | "name": "appender", 405 | "first_cutoff": self.first_cutoff.to_json(), 406 | "number_from_first": self.number_from_first, 407 | "second_cutoff": self.second_cutoff.to_json(), 408 | } 409 | 410 | 411 | class MetaCutoffMerger(Cutoff): # pylint: disable=too-few-public-methods 412 | """ 413 | Represents a meta-cutoff that merges two different cutoff conditions. 414 | 415 | Methods: 416 | __init__(first_cutoff: Cutoff, second_cutoff: Cutoff) -> None: 417 | Initialize the MetaCutoffMerger instance. 418 | 419 | is_meta() -> bool: 420 | Check if the cutoff condition is a meta-cutoff. 421 | 422 | to_json(): 423 | Convert the cutoff condition to a JSON representation. 424 | 425 | """ 426 | 427 | def __init__(self, first_cutoff: Cutoff, second_cutoff: Cutoff) -> None: 428 | """ 429 | Initialize the MetaCutoffMerger instance. 430 | 431 | Args: 432 | first_cutoff (Cutoff): The first cutoff condition. 433 | second_cutoff (Cutoff): The second cutoff condition. 434 | 435 | Returns: 436 | None 437 | 438 | """ 439 | super().__init__() 440 | self.first_cutoff = first_cutoff 441 | self.second_cutoff = second_cutoff 442 | 443 | def is_meta(self) -> bool: 444 | """ 445 | Check if the cutoff condition is a meta-cutoff. 446 | 447 | Returns: 448 | bool: True, indicating that this is a meta-cutoff. 449 | 450 | """ 451 | return True 452 | 453 | def to_json(self): 454 | """ 455 | Convert the cutoff condition to a JSON representation. 456 | 457 | Returns: 458 | dict: A JSON representation of the cutoff condition. 459 | 460 | """ 461 | return { 462 | "name": "merger", 463 | "first_cutoff": self.first_cutoff.to_json(), 464 | "second_cutoff": self.second_cutoff.to_json(), 465 | } 466 | 467 | 468 | class MetaCutoffRepeater(Cutoff): # pylint: disable=too-few-public-methods 469 | """ 470 | Represents a meta-cutoff that repeats a sub-cutoff condition multiple times. 471 | 472 | Methods: 473 | __init__(sub_cutoff: Cutoff, repeat: int) -> None: 474 | Initialize the MetaCutoffRepeater instance. 475 | 476 | is_meta() -> bool: 477 | Check if the cutoff condition is a meta-cutoff. 478 | 479 | to_json(): 480 | Convert the cutoff condition to a JSON representation. 481 | 482 | """ 483 | 484 | def __init__(self, sub_cutoff: Cutoff, repeat: int) -> None: 485 | """ 486 | Initialize the MetaCutoffRepeater instance. 487 | 488 | Args: 489 | sub_cutoff (Cutoff): The sub-cutoff condition to be repeated. 490 | repeat (int): The number of times to repeat the sub-cutoff. 491 | 492 | Returns: 493 | None 494 | 495 | """ 496 | super().__init__() 497 | self.sub_cutoff = sub_cutoff 498 | self.repeat = repeat 499 | 500 | def is_meta(self) -> bool: 501 | """ 502 | Check if the cutoff condition is a meta-cutoff. 503 | 504 | Returns: 505 | bool: True, indicating that this is a meta-cutoff. 506 | 507 | """ 508 | return True 509 | 510 | def to_json(self): 511 | """ 512 | Convert the cutoff condition to a JSON representation. 513 | 514 | Returns: 515 | dict: A JSON representation of the cutoff condition. 516 | 517 | """ 518 | return { 519 | "name": "repeater", 520 | "sub_cutoff": self.sub_cutoff.to_json(), 521 | "repeat": self.repeat, 522 | } 523 | -------------------------------------------------------------------------------- /qaekwy/model/function.py: -------------------------------------------------------------------------------- 1 | """Math Functions Module 2 | 3 | This module defines various mathematical functions that can be applied to expressions 4 | or arrays of expressions. 5 | 6 | Functions: 7 | maximum(expr: ExpressionArray) -> Expression: Returns an expression representing the max value. 8 | minimum(expr: ExpressionArray) -> Expression: Returns an expression representing the min value. 9 | sum_of(expr: ExpressionArray) -> Expression: Returns an expression representing the sum. 10 | absolute(expr: Expression) -> Expression: Returns an expression representing the absolute value. 11 | power(expr: Expression, val) -> Expression: Returns an expression representing pow(expr, val). 12 | nroot(expr: Expression, val) -> Expression: Returns an expression representing the nth root. 13 | sqr(expr: Expression) -> Expression: Returns an expression representing the square. 14 | sqrt(expr: Expression) -> Expression: Returns an expression representing the square root. 15 | sin(expr: Expression) -> Expression: Returns an expression representing the sine. 16 | cos(expr: Expression) -> Expression: Returns an expression representing the cosine. 17 | tan(expr: Expression) -> Expression: Returns an expression representing the tangent. 18 | asin(expr: Expression) -> Expression: Returns an expression representing the arcsine. 19 | acos(expr: Expression) -> Expression: Returns an expression representing the arccosine. 20 | atan(expr: Expression) -> Expression: Returns an expression representing the arctangent. 21 | log(expr: Expression) -> Expression: Returns an expression representing the natural logarithm. 22 | exp(expr: Expression) -> Expression: Returns an expression representing the exponential. 23 | 24 | """ 25 | 26 | from qaekwy.model.variable.variable import Expression, ExpressionArray 27 | 28 | 29 | def maximum(expr: ExpressionArray) -> Expression: 30 | """ 31 | Returns an expression representing the maximum value of an expression array. 32 | 33 | Args: 34 | expr (ExpressionArray): The array of expressions. 35 | 36 | Returns: 37 | Expression: An expression representing the maximum value. 38 | """ 39 | return Expression(f"max({expr})") 40 | 41 | 42 | def minimum(expr: ExpressionArray) -> Expression: 43 | """ 44 | Returns an expression representing the minimum value of an expression array. 45 | 46 | Args: 47 | expr (ExpressionArray): The array of expressions. 48 | 49 | Returns: 50 | Expression: An expression representing the minimum value. 51 | """ 52 | return Expression(f"min({expr})") 53 | 54 | 55 | def sum_of(expr: ExpressionArray) -> Expression: 56 | """ 57 | Returns an expression representing the sum of an expression array. 58 | 59 | Args: 60 | expr (ExpressionArray): The array of expressions. 61 | 62 | Returns: 63 | Expression: An expression representing the sum. 64 | """ 65 | return Expression(f"sum({expr})") 66 | 67 | 68 | def absolute(expr: Expression) -> Expression: 69 | """ 70 | Returns an expression representing the absolute value of an expression. 71 | 72 | Args: 73 | expr (Expression): The expression. 74 | 75 | Returns: 76 | Expression: An expression representing the absolute value. 77 | """ 78 | return Expression(f"abs({expr})") 79 | 80 | 81 | def power(expr: Expression, val) -> Expression: 82 | """ 83 | Returns an expression representing the exponentiation of an expression by a value. 84 | 85 | Args: 86 | expr (Expression): The expression. 87 | val: The exponent value. 88 | 89 | Returns: 90 | Expression: An expression representing the exponentiation result. 91 | """ 92 | 93 | return Expression(f"pow({expr}, {val})") 94 | 95 | 96 | def nroot(expr: Expression, val) -> Expression: 97 | """ 98 | Returns an expression representing the nth root of an expression by a value. 99 | 100 | Args: 101 | expr (Expression): The expression. 102 | val: The root value. 103 | 104 | Returns: 105 | Expression: An expression representing the nth root result. 106 | """ 107 | return Expression(f"nroot({expr}, {val})") 108 | 109 | 110 | def sqr(expr: Expression) -> Expression: 111 | """ 112 | Returns an expression representing the square of an expression. 113 | 114 | Args: 115 | expr (Expression): The expression. 116 | 117 | Returns: 118 | Expression: An expression representing the square. 119 | """ 120 | return Expression(f"sqr({expr})") 121 | 122 | 123 | def sqrt(expr: Expression) -> Expression: 124 | """ 125 | Returns an expression representing the square root of an expression. 126 | 127 | Args: 128 | expr (Expression): The expression. 129 | 130 | Returns: 131 | Expression: An expression representing the square root. 132 | """ 133 | return Expression(f"sqrt({expr})") 134 | 135 | 136 | def sin(expr: Expression) -> Expression: 137 | """ 138 | Returns an expression representing the sine of an expression. 139 | 140 | Args: 141 | expr (Expression): The expression. 142 | 143 | Returns: 144 | Expression: An expression representing the sine value. 145 | """ 146 | return Expression(f"sin({expr})") 147 | 148 | 149 | def cos(expr: Expression) -> Expression: 150 | """ 151 | Returns an expression representing the cosine of an expression. 152 | 153 | Args: 154 | expr (Expression): The expression. 155 | 156 | Returns: 157 | Expression: An expression representing the cosine value. 158 | """ 159 | return Expression(f"cos({expr})") 160 | 161 | 162 | def tan(expr: Expression) -> Expression: 163 | """ 164 | Returns an expression representing the tangent of an expression. 165 | 166 | Args: 167 | expr (Expression): The expression. 168 | 169 | Returns: 170 | Expression: An expression representing the tangent value. 171 | """ 172 | return Expression(f"tan({expr})") 173 | 174 | 175 | def asin(expr: Expression) -> Expression: 176 | """ 177 | Returns an expression representing the arcsine of an expression. 178 | 179 | Args: 180 | expr (Expression): The expression. 181 | 182 | Returns: 183 | Expression: An expression representing the arcsine value. 184 | """ 185 | return Expression(f"asin({expr})") 186 | 187 | 188 | def acos(expr: Expression) -> Expression: 189 | """ 190 | Returns an expression representing the arccosine of an expression. 191 | 192 | Args: 193 | expr (Expression): The expression. 194 | 195 | Returns: 196 | Expression: An expression representing the arccosine value. 197 | """ 198 | return Expression(f"acos({expr})") 199 | 200 | 201 | def atan(expr: Expression) -> Expression: 202 | """ 203 | Returns an expression representing the arctangent of an expression. 204 | 205 | Args: 206 | expr (Expression): The expression. 207 | 208 | Returns: 209 | Expression: An expression representing the arctangent value. 210 | """ 211 | return Expression(f"atan({expr})") 212 | 213 | 214 | def log(expr: Expression) -> Expression: 215 | """ 216 | Returns an expression representing the natural logarithm of an expression. 217 | 218 | Args: 219 | expr (Expression): The expression. 220 | 221 | Returns: 222 | Expression: An expression representing the natural logarithm value. 223 | """ 224 | return Expression(f"log({expr})") 225 | 226 | 227 | def exp(expr: Expression) -> Expression: 228 | """ 229 | Returns an expression representing the exponential function of an expression. 230 | 231 | Args: 232 | expr (Expression): The expression. 233 | 234 | Returns: 235 | Expression: An expression representing the exponential function value. 236 | """ 237 | return Expression(f"exp({expr})") 238 | -------------------------------------------------------------------------------- /qaekwy/model/modeller.py: -------------------------------------------------------------------------------- 1 | """ 2 | This module provides the Modeller class for building optimization models. 3 | 4 | Classes: 5 | Modeller: Represents a modeller used to construct optimization models. 6 | 7 | Methods: 8 | add_variable(variable: Union[Variable, ArrayVariable]) -> Modeller: 9 | Adds a variable to the optimization model. 10 | 11 | add_constraint(constraint: Union[AbstractConstraint, Expression]) -> Modeller: 12 | Adds a constraint to the optimization model. 13 | 14 | add_objective(objective: Union[SpecificMinimum, SpecificMaximum]) -> Modeller: 15 | Adds an objective to the optimization model. 16 | 17 | set_searcher(searcher: SearcherType) -> Modeller: 18 | Sets the searcher type for optimization. 19 | 20 | set_cutoff(cutoff: Cutoff) -> Modeller: 21 | Sets a cutoff condition for optimization. 22 | 23 | set_callback_url(callback_url: str) -> Modeller: 24 | Sets a callback URL for optimization. 25 | 26 | to_json() -> dict: 27 | Converts the optimization model to a JSON representation. 28 | 29 | Note: 30 | Refer to the individual method documentation for more details about their usage. 31 | 32 | """ 33 | 34 | from typing import Union 35 | from qaekwy.exception.model_failure import ModelFailure 36 | from qaekwy.model.constraint.abstract_constraint import AbstractConstraint 37 | from qaekwy.model.constraint.relational import RelationalExpression 38 | from qaekwy.model.cutoff import Cutoff 39 | from qaekwy.model.searcher import SearcherType 40 | from qaekwy.model.specific import SpecificMaximum, SpecificMinimum 41 | from qaekwy.model.variable.variable import ArrayVariable, Expression, Variable 42 | 43 | 44 | class Modeller: 45 | """ 46 | Represents a modeller used to build optimization models. 47 | 48 | Attributes: 49 | constraint_list (list[AbstractConstraint]): A list of constraints. 50 | variable_list (list[Union[Variable, ArrayVariable]]): A list of variables. 51 | objective_list (list[Union[SpecificMinimum, SpecificMaximum]]): A list of objectives. 52 | searcher (SearcherType): The type of searcher to be used for optimization. 53 | cutoff (Cutoff): The cutoff condition for stopping the optimization. 54 | callback_url (str): The URL to which the optimization callback will be sent. 55 | 56 | Methods: 57 | add_variable(variable: Union[Variable, ArrayVariable]): Add a variable to the model. 58 | add_constraint(constraint: Union[AbstractConstraint, Expression]): Add a constraint to the model. 59 | add_objective(objective: Union[SpecificMinimum, SpecificMaximum]): Add an objective. 60 | set_searcher(searcher: SearcherType): Set the searcher type for optimization. 61 | set_cutoff(cutoff: Cutoff): Set the cutoff condition for optimization. 62 | set_callback_url(callback_url: str): Set the callback URL for optimization. 63 | to_json() -> dict: Convert the modeller and its components to a JSON representation. 64 | 65 | """ 66 | 67 | def __init__(self) -> None: 68 | """ 69 | Initialize a Modeller instance. 70 | """ 71 | self.constraint_list = [] 72 | self.variable_list = [] 73 | self.objective_list = [] 74 | self.searcher = None 75 | self.cutoff = None 76 | self.callback_url = None 77 | 78 | def add_variable(self, variable: Union[Variable, ArrayVariable]): 79 | """ 80 | Add a variable to the model. 81 | 82 | Args: 83 | variable (Union[Variable, ArrayVariable]): The variable to be added. 84 | 85 | Returns: 86 | Modeller: The modeller instance for method chaining. 87 | """ 88 | self.variable_list.append(variable) 89 | return self 90 | 91 | def add_constraint(self, constraint: Union[AbstractConstraint, Expression]): 92 | """ 93 | Add a constraint to the model. 94 | 95 | Args: 96 | constraint (Union[AbstractConstraint, Expression]): The constraint to be added. 97 | 98 | Returns: 99 | Modeller: The modeller instance for method chaining. 100 | """ 101 | self.constraint_list.append( 102 | RelationalExpression(constraint) 103 | if isinstance(constraint, Expression) 104 | else constraint 105 | ) 106 | return self 107 | 108 | def add_objective(self, objective: Union[SpecificMinimum, SpecificMaximum]): 109 | """ 110 | Add an objective to the model. 111 | 112 | Args: 113 | objective (Union[SpecificMinimum, SpecificMaximum]): The objective to be added. 114 | 115 | Returns: 116 | Modeller: The modeller instance for method chaining. 117 | """ 118 | self.objective_list.append(objective) 119 | return self 120 | 121 | def set_searcher(self, searcher: SearcherType): 122 | """ 123 | Set the searcher type for optimization. 124 | 125 | Args: 126 | searcher (SearcherType): The type of searcher to be used. 127 | 128 | Returns: 129 | Modeller: The modeller instance for method chaining. 130 | """ 131 | self.searcher = searcher 132 | return self 133 | 134 | def set_cutoff(self, cutoff: Cutoff): 135 | """ 136 | Set the cutoff condition for optimization. 137 | 138 | Args: 139 | cutoff (Cutoff): The cutoff condition. 140 | 141 | Returns: 142 | Modeller: The modeller instance for method chaining. 143 | """ 144 | self.cutoff = cutoff 145 | return self 146 | 147 | def set_callback_url(self, callback_url: str): 148 | """ 149 | Set the callback URL to call after model optimization. 150 | 151 | Args: 152 | callback_url (str): The callback URL. 153 | 154 | Returns: 155 | Modeller: The modeller instance for method chaining. 156 | """ 157 | self.callback_url = callback_url 158 | return self 159 | 160 | def to_json(self) -> dict: 161 | """ 162 | Convert the modeller and its components to a JSON representation. 163 | 164 | Returns: 165 | dict: A dictionary representing the modeller in JSON format. 166 | """ 167 | res = {} 168 | 169 | if self.searcher is None: 170 | raise ModelFailure( 171 | "Not any SearcherType has been set (through 'set_searcher' method of 'Modeller')." 172 | ) 173 | 174 | res["searcher"] = self.searcher.value 175 | 176 | res["var"] = [] 177 | for var_elem in self.variable_list: 178 | res["var"].append(var_elem.to_json()) 179 | 180 | res["constraint"] = [] 181 | for constraint_elem in self.constraint_list: 182 | res["constraint"].append(constraint_elem.to_json()) 183 | 184 | res["specific"] = [] 185 | for specific_elem in self.objective_list: 186 | res["specific"].append(specific_elem.to_json()) 187 | 188 | if self.cutoff is not None: 189 | res[ 190 | "meta_cutoff" if self.cutoff.is_meta() else "cutoff" 191 | ] = self.cutoff.to_json() 192 | 193 | if self.callback_url is not None: 194 | res["callback_url"] = str(self.callback_url) 195 | 196 | res["solution_limit"] = 1 197 | 198 | return res 199 | -------------------------------------------------------------------------------- /qaekwy/model/relation.py: -------------------------------------------------------------------------------- 1 | """RelationType Module 2 | 3 | This module defines the RelationType enum, which represents different 4 | types of relational comparisons. 5 | 6 | Enums: 7 | RelationType: Represents different types of relational comparisons. 8 | 9 | """ 10 | from enum import Enum 11 | 12 | 13 | class RelationType(Enum): 14 | """ 15 | Represents different types of relational comparisons. 16 | 17 | The RelationType enum defines symbolic representations of common relational comparisons 18 | used in constraint-based modelling, such as greater than, greater than or equal to, 19 | equal to, not equal to, less than or equal to, and less than. 20 | 21 | Enum Members: 22 | GT (str): Greater than. 23 | GE (str): Greater than or equal to. 24 | EQ (str): Equal to. 25 | NE (str): Not equal to. 26 | LE (str): Less than or equal to. 27 | LT (str): Less than. 28 | 29 | Example: 30 | relation = RelationType.GE # Represents "greater than or equal to" 31 | """ 32 | 33 | GT = "GT" 34 | GE = "GE" 35 | EQ = "EQ" 36 | NE = "NE" 37 | LE = "LE" 38 | LT = "LT" 39 | -------------------------------------------------------------------------------- /qaekwy/model/searcher.py: -------------------------------------------------------------------------------- 1 | """SearcherType Module 2 | 3 | This module defines the SearcherType enum, which represents different types of search algorithms 4 | used in constraint-based modeling and optimization. 5 | 6 | Enums: 7 | SearcherType: Represents different types of search algorithms. 8 | 9 | """ 10 | from enum import Enum 11 | 12 | 13 | class SearcherType(Enum): 14 | """ 15 | Represents different types of search algorithms. 16 | 17 | The SearcherType enum defines symbolic representations of various search algorithms used 18 | in constraint-based modeling and optimization, such as Depth-First Search (DFS), 19 | Branch and Bound (BAB), Limited Discrepancy Search (LDS), Portfolio-Based Search (PBS), 20 | and Randomized Binary Search (RBS). 21 | 22 | Enum Members: 23 | DFS (str): Depth-First Search algorithm. 24 | BAB (str): Branch and Bound algorithm. 25 | LDS (str): Limited Discrepancy Search algorithm. 26 | PBS (str): Portfolio-Based Search algorithm. 27 | RBS (str): Randomized Binary Search algorithm. 28 | 29 | Example: 30 | searcher = SearcherType.LDS # Represents "Limited Discrepancy Search" 31 | """ 32 | 33 | DFS = "DFS" 34 | BAB = "BAB" 35 | LDS = "LDS" 36 | PBS = "PBS" 37 | RBS = "RBS" 38 | -------------------------------------------------------------------------------- /qaekwy/model/specific.py: -------------------------------------------------------------------------------- 1 | """Specific Constraints Module 2 | 3 | This module defines specific constraint classes that represent constraints for minimizing 4 | and maximizing specific variables. 5 | 6 | Classes: 7 | SpecificMinimum: Represents a constraint to minimize a specific variable. 8 | SpecificMaximum: Represents a constraint to maximize a specific variable. 9 | 10 | """ 11 | from qaekwy.model.constraint.abstract_constraint import AbstractConstraint 12 | from qaekwy.model.variable.variable import Variable 13 | 14 | 15 | class SpecificMinimum(AbstractConstraint): 16 | """ 17 | Represents a constraint to minimize a specific variable. 18 | 19 | The SpecificMinimum class defines a constraint that aims to minimize the value 20 | of a specific variable. 21 | 22 | It inherits from the AbstractConstraint class and provides a method to convert the constraint 23 | to a JSON representation suitable for serialization. 24 | 25 | Args: 26 | variable (Variable): The variable to be minimized. 27 | constraint_name (str, optional): The name of the constraint. 28 | 29 | Example: 30 | specific_min = SpecificMinimum(my_variable, "minimize_constraint") 31 | """ 32 | 33 | def __init__(self, variable: Variable, constraint_name=None) -> None: 34 | super().__init__(constraint_name) 35 | self.variable = variable 36 | 37 | def to_json(self): 38 | """ 39 | Converts the constraint to a JSON representation. 40 | 41 | Returns: 42 | dict: A JSON representation of the constraint. 43 | """ 44 | return {"var": self.variable.var_name, "type": "minimize"} 45 | 46 | 47 | class SpecificMaximum(AbstractConstraint): 48 | """ 49 | Represents a constraint to maximize a specific variable. 50 | 51 | The SpecificMaximum class defines a constraint that aims to maximize the value 52 | of a specific variable. 53 | 54 | It inherits from the AbstractConstraint class and provides a method to convert the constraint 55 | to a JSON representation suitable for serialization. 56 | 57 | Args: 58 | variable (Variable): The variable to be maximized. 59 | constraint_name (str, optional): The name of the constraint. 60 | 61 | Example: 62 | specific_max = SpecificMaximum(my_variable, "maximize_constraint") 63 | """ 64 | 65 | def __init__(self, variable: Variable, constraint_name=None) -> None: 66 | super().__init__(constraint_name) 67 | self.variable = variable 68 | 69 | def to_json(self): 70 | """ 71 | Converts the constraint to a JSON representation. 72 | 73 | Returns: 74 | dict: A JSON representation of the constraint. 75 | """ 76 | return {"var": self.variable.var_name, "type": "maximize"} 77 | -------------------------------------------------------------------------------- /qaekwy/model/variable/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alex-87/qaekwy-python/c4a76740e69d295ec55d0dc5ebfc8355d6e7a679/qaekwy/model/variable/__init__.py -------------------------------------------------------------------------------- /qaekwy/model/variable/boolean.py: -------------------------------------------------------------------------------- 1 | """BooleanVariable Module 2 | 3 | This module defines classes related to boolean variables. 4 | 5 | Classes: 6 | BooleanVariable: Represents a boolean variable. 7 | BooleanExpressionVariable: Represents a boolean variable defined by an expression. 8 | BooleanVariableArray: Represents an array of boolean variables. 9 | 10 | """ 11 | from qaekwy.model.variable.branch import BranchVal, BranchVar 12 | from qaekwy.model.variable.variable import ( 13 | ArrayVariable, 14 | ExpressionVariable, 15 | Variable, 16 | VariableType, 17 | ) 18 | 19 | 20 | class BooleanVariable(Variable): # pylint: disable=too-few-public-methods 21 | """ 22 | Represents a boolean variable. 23 | 24 | The BooleanVariable class represents a boolean variable. 25 | 26 | Args: 27 | var_name (str): The name of the variable. 28 | branch_val (BranchVal): The brancher value strategy. 29 | 30 | Example: 31 | my_bool_var = BooleanVariable("b1", branch_val=BranchVal.VAL_MIN) 32 | """ 33 | 34 | def __init__(self, var_name: str, branch_val: BranchVal) -> None: 35 | super().__init__(var_name, 0, 1, VariableType.BOOLEAN, branch_val) 36 | 37 | 38 | class BooleanExpressionVariable( 39 | ExpressionVariable 40 | ): # pylint: disable=too-few-public-methods 41 | """ 42 | Represents a boolean variable defined by an expression. 43 | 44 | The BooleanExpressionVariable class extends the functionality of the 45 | ExpressionVariable class to represent boolean variables that are defined 46 | by an expression. 47 | 48 | Args: 49 | var_name (str): The name of the variable. 50 | expression: The expression defining the variable. 51 | branch_val (BranchVal): The brancher value strategy. 52 | 53 | Example: 54 | expr = Expression(x[3] > 5) 55 | my_expr_bool_var = 56 | BooleanExpressionVariable("b2", expression=expr, branch_val=BranchVal.VAL_MID) 57 | """ 58 | 59 | def __init__(self, var_name: str, expression: str, branch_val: BranchVal) -> None: 60 | super().__init__(var_name, expression, VariableType.BOOLEAN, branch_val) 61 | 62 | 63 | class BooleanVariableArray(ArrayVariable): # pylint: disable=too-few-public-methods 64 | """ 65 | Represents an array of boolean variables. 66 | 67 | The BooleanVariableArray class represents an array of boolean variables. 68 | 69 | Args: 70 | var_name (str): The name of the variable. 71 | length (int): The length of the array. 72 | branch_var (BranchVar): The brancher variable strategy. 73 | branch_val (BranchVal): The brancher value strategy. 74 | 75 | Example: 76 | my_bool_array = BooleanVariableArray("bool_arr", length=3, branch_var=BranchVar.VAR_MIN, 77 | branch_val=BranchVal.VAL_RND) 78 | """ 79 | 80 | def __init__( 81 | self, var_name: str, length: int, branch_var: BranchVar, branch_val: BranchVal 82 | ) -> None: 83 | super().__init__( 84 | var_name, VariableType.BOOLEAN_ARRAY, length, 0, 1, branch_var, branch_val 85 | ) 86 | -------------------------------------------------------------------------------- /qaekwy/model/variable/branch.py: -------------------------------------------------------------------------------- 1 | """Branch Module 2 | 3 | This module defines classes related to brancher strategies. 4 | 5 | Classes: 6 | BranchVal: Represents brancher value strategies. 7 | BranchVar: Represents brancher variable strategies. 8 | BranchIntegerVal: Represents brancher value strategies specific to integers. 9 | BranchFloatVal: Represents brancher value strategies specific to floats. 10 | BranchBooleanVal: Represents brancher value strategies specific to booleans. 11 | BranchIntegerVar: Represents brancher variable strategies specific to integers. 12 | BranchFloatVar: Represents brancher variable strategies specific to floats. 13 | BranchBooleanVar: Represents brancher variable strategies specific to booleans. 14 | 15 | """ 16 | 17 | from enum import Enum 18 | 19 | 20 | class BranchVal(Enum): # pylint: disable=too-few-public-methods 21 | """ 22 | Represents brancher value strategies. 23 | 24 | The BranchVal class defines enumeration values that represent different 25 | strategies for selecting brancher values during the search process. 26 | """ 27 | 28 | 29 | class BranchVar(Enum): # pylint: disable=too-few-public-methods 30 | """ 31 | Represents brancher variable strategies. 32 | 33 | The BranchVar class defines enumeration values that represent different 34 | strategies for selecting brancher variables during the search process. 35 | """ 36 | 37 | 38 | class BranchIntegerVal(BranchVal): 39 | """ 40 | Represents brancher value strategies specific to integers. 41 | 42 | The BranchIntegerVal class extends the BranchVal class and defines 43 | enumeration values that represent specific brancher value strategies 44 | for integer variables. 45 | """ 46 | 47 | VAL_RND = "VAL_RND" 48 | VAL_MIN = "VAL_MIN" 49 | VAL_MED = "VAL_MED" 50 | VAL_MAX = "VAL_MAX" 51 | VALUES_MIN = "VALUES_MIN" 52 | VALUES_MAX = "VALUES_MAX" 53 | VAL_RANGE_MIN = "VAL_RANGE_MIN" 54 | VAL_RANGE_MAX = "VAL_RANGE_MAX" 55 | VAL_SPLIT_MIN = "VAL_SPLIT_MIN" 56 | VAL_SPLIT_MAX = "VAL_SPLIT_MAX" 57 | 58 | 59 | class BranchFloatVal(BranchVal): 60 | """ 61 | Represents brancher value strategies specific to floats. 62 | 63 | The BranchFloatVal class extends the BranchVal class and defines 64 | enumeration values that represent specific brancher value strategies 65 | for float variables. 66 | """ 67 | 68 | VAL_RND = "VAL_RND" 69 | VAL_MIN = "VAL_MIN" 70 | VAL_MAX = "VAL_MAX" 71 | VAL_SPLIT_MIN = "VAL_SPLIT_MIN" 72 | VAL_SPLIT_MAX = "VAL_SPLIT_MAX" 73 | 74 | 75 | class BranchBooleanVal(BranchVal): 76 | """ 77 | Represents brancher value strategies specific to booleans. 78 | 79 | The BranchBooleanVal class extends the BranchVal class and defines 80 | enumeration values that represent specific brancher value strategies 81 | for boolean variables. 82 | """ 83 | 84 | VAL_RND = "VAL_RND" 85 | VAL_MIN = "VAL_MIN" 86 | VAL_MAX = "VAL_MAX" 87 | 88 | 89 | class BranchIntegerVar(BranchVar): 90 | """ 91 | Represents brancher variable strategies specific to integers. 92 | 93 | The BranchIntegerVar class extends the BranchVar class and defines 94 | enumeration values that represent specific brancher variable strategies 95 | for integer variables. 96 | """ 97 | 98 | VAR_NONE = "VAR_NONE" 99 | VAR_RND = "VAR_RND" 100 | VAR_SIZE_MIN = "VAR_SIZE_MIN" 101 | VAR_SIZE_MAX = "VAR_SIZE_MAX" 102 | VAR_REGRET_MIN_MIN = "VAR_REGRET_MIN_MIN" 103 | VAR_REGRET_MIN_MAX = "VAR_REGRET_MIN_MAX" 104 | VAR_DEGREE_MIN = "VAR_DEGREE_MIN" 105 | VAR_DEGREE_MAX = "VAR_DEGREE_MAX" 106 | VAR_MIN_MIN = "VAR_MIN_MIN" 107 | VAR_MIN_MAX = "VAR_MIN_MAX" 108 | VAR_MAX_MIN = "VAR_MAX_MIN" 109 | VAR_MAX_MAX = "VAR_MAX_MAX" 110 | VAR_DEGREE_SIZE_MIN = "VAR_DEGREE_SIZE_MIN" 111 | VAR_DEGREE_SIZE_MAX = "VAR_DEGREE_SIZE_MAX" 112 | 113 | 114 | class BranchFloatVar(BranchVar): 115 | """ 116 | Represents brancher variable strategies specific to floats. 117 | 118 | The BranchFloatVar class extends the BranchVar class and defines 119 | enumeration values that represent specific brancher variable strategies 120 | for float variables. 121 | """ 122 | 123 | VAR_RND = "VAR_RND" 124 | VAR_SIZE_MIN = "VAR_SIZE_MIN" 125 | VAR_SIZE_MAX = "VAR_SIZE_MAX" 126 | VAR_DEGREE_MIN = "VAR_DEGREE_MIN" 127 | VAR_DEGREE_MAX = "VAR_DEGREE_MAX" 128 | VAR_MIN_MIN = "VAR_MIN_MIN" 129 | VAR_MIN_MAX = "VAR_MIN_MAX" 130 | VAR_MAX_MIN = "VAR_MAX_MIN" 131 | VAR_MAX_MAX = "VAR_MAX_MAX" 132 | VAR_DEGREE_SIZE_MIN = "VAR_DEGREE_SIZE_MIN" 133 | VAR_DEGREE_SIZE_MAX = "VAR_DEGREE_SIZE_MAX" 134 | 135 | 136 | class BranchBooleanVar(BranchVar): 137 | """ 138 | Represents brancher variable strategies specific to booleans. 139 | 140 | The BranchBooleanVar class extends the BranchVar class and defines 141 | enumeration values that represent specific brancher variable strategies 142 | for boolean variables. 143 | """ 144 | 145 | VAR_RND = "VAR_RND" 146 | VAR_DEGREE_SIZE_MIN = "VAR_DEGREE_SIZE_MIN" 147 | VAR_DEGREE_SIZE_MAX = "VAR_DEGREE_SIZE_MAX" 148 | -------------------------------------------------------------------------------- /qaekwy/model/variable/float.py: -------------------------------------------------------------------------------- 1 | """FloatVariable Module 2 | 3 | This module defines classes related to float variables. 4 | 5 | Classes: 6 | FloatVariable: Represents a float variable. 7 | FloatExpressionVariable: Represents a float variable defined by an expression. 8 | FloatVariableArray: Represents an array of float variables. 9 | 10 | """ 11 | 12 | from typing import Optional 13 | from qaekwy.model.variable.branch import ( 14 | BranchFloatVal, 15 | BranchFloatVar, 16 | BranchVal, 17 | ) 18 | from qaekwy.model.variable.variable import ( 19 | ArrayVariable, 20 | ExpressionVariable, 21 | Variable, 22 | VariableType, 23 | ) 24 | 25 | 26 | class FloatVariable(Variable): # pylint: disable=too-few-public-methods 27 | """ 28 | Represents a float variable. 29 | 30 | The FloatVariable class represents a float variable. 31 | 32 | Args: 33 | var_name (str): The name of the variable. 34 | domain_low (float, optional): The lower bound of the variable's domain. 35 | domain_high (float, optional): The upper bound of the variable's domain. 36 | specific_domain (list, optional): A specific domain for the variable. 37 | branch_val (BranchVal, optional): The brancher value strategy. 38 | 39 | Example: 40 | my_float_var = 41 | FloatVariable("x", domain_low=0.0, domain_high=1.0, branch_val=BranchFloatVal.VAL_RND) 42 | """ 43 | 44 | def __init__( # pylint: disable=too-many-arguments 45 | self, 46 | var_name: str, 47 | domain_low: Optional[float] = None, 48 | domain_high: Optional[float] = None, 49 | specific_domain: Optional[list] = None, 50 | branch_val: BranchVal = BranchFloatVal.VAL_RND, 51 | ) -> None: 52 | super().__init__( 53 | var_name, 54 | domain_low, 55 | domain_high, 56 | specific_domain, 57 | VariableType.FLOAT, 58 | branch_val, 59 | ) 60 | 61 | 62 | class FloatExpressionVariable( 63 | ExpressionVariable 64 | ): # pylint: disable=too-few-public-methods 65 | """ 66 | Represents a float variable defined by an expression. 67 | 68 | The FloatExpressionVariable class extends the functionality of the ExpressionVariable 69 | class to represent float variables that are defined by an expression. 70 | 71 | Args: 72 | var_name (str): The name of the variable. 73 | expression: The expression defining the variable. 74 | branch_val (BranchVal, optional): The brancher value strategy. 75 | 76 | Example: 77 | expr = Expression(0.5 * x + 0.2) 78 | my_expr_float_var = FloatExpressionVariable("y", expression=expr, 79 | branch_val=BranchFloatVal.VAL_MID) 80 | """ 81 | 82 | def __init__( 83 | self, 84 | var_name: str, 85 | expression: str, 86 | branch_val: BranchVal = BranchFloatVal.VAL_RND, 87 | ) -> None: 88 | super().__init__(var_name, expression, VariableType.FLOAT, branch_val) 89 | 90 | 91 | class FloatVariableArray(ArrayVariable): 92 | """ 93 | Represents an array of float variables. 94 | 95 | The FloatVariableArray class represents an array of float variables. 96 | 97 | Args: 98 | var_name (str): The name of the variable. 99 | length (int): The length of the array. 100 | domain_low (float, optional): The lower bound of the variables' domain. 101 | domain_high (float, optional): The upper bound of the variables' domain. 102 | branch_var (BranchFloatVar, optional): The brancher variable strategy. 103 | branch_val (BranchVal, optional): The brancher value strategy. 104 | 105 | Example: 106 | my_float_array = FloatVariableArray("arr", length=5, domain_low=0.0, domain_high=10.0, 107 | branch_var=BranchFloatVar.VAR_RND) 108 | """ 109 | 110 | def __init__( # pylint: disable=too-many-arguments 111 | self, 112 | var_name: str, 113 | length: int, 114 | domain_low: Optional[float] = None, 115 | domain_high: Optional[float] = None, 116 | branch_var: BranchFloatVar = BranchFloatVar.VAR_RND, 117 | branch_val: BranchVal = BranchFloatVal.VAL_RND, 118 | ) -> None: 119 | super().__init__( 120 | var_name, 121 | VariableType.FLOAT_ARRAY, 122 | length, 123 | domain_low, 124 | domain_high, 125 | branch_var, 126 | branch_val, 127 | ) 128 | -------------------------------------------------------------------------------- /qaekwy/model/variable/integer.py: -------------------------------------------------------------------------------- 1 | """IntegerVariable Module 2 | 3 | This module defines classes related to integer variables. 4 | 5 | Classes: 6 | IntegerVariable: Represents an integer variable. 7 | IntegerExpressionVariable: Represents an integer variable defined by an expression. 8 | IntegerVariableArray: Represents an array of integer variables. 9 | 10 | """ 11 | 12 | from typing import Optional 13 | from qaekwy.model.variable.branch import ( 14 | BranchIntegerVal, 15 | BranchIntegerVar, 16 | BranchVal, 17 | BranchVar, 18 | ) 19 | from qaekwy.model.variable.variable import ( 20 | ArrayVariable, 21 | ExpressionVariable, 22 | Variable, 23 | VariableType, 24 | ) 25 | 26 | 27 | class IntegerVariable(Variable): # pylint: disable=too-few-public-methods 28 | """ 29 | Represents an integer variable. 30 | 31 | The IntegerVariable class represents an integer variable. 32 | 33 | Args: 34 | var_name (str): The name of the variable. 35 | domain_low (int, optional): The lower bound of the variable's domain. 36 | domain_high (int, optional): The upper bound of the variable's domain. 37 | specific_domain (list, optional): A specific domain for the variable. 38 | branch_val (BranchVal, optional): The brancher value strategy. 39 | 40 | Example: 41 | my_integer_var = 42 | IntegerVariable("x", domain_low=1, domain_high=10, branch_val=BranchIntegerVal.VAL_RND) 43 | """ 44 | 45 | def __init__( # pylint: disable=too-many-arguments 46 | self, 47 | var_name: str, 48 | domain_low: Optional[int] = None, 49 | domain_high: Optional[int] = None, 50 | specific_domain: Optional[list] = None, 51 | branch_val: BranchVal = BranchIntegerVal.VAL_RND, 52 | ) -> None: 53 | super().__init__( 54 | var_name=var_name, 55 | domain_low=domain_low, 56 | domain_high=domain_high, 57 | specific_domain=specific_domain, 58 | var_type=VariableType.INTEGER, 59 | branch_val=branch_val, 60 | ) 61 | 62 | 63 | class IntegerExpressionVariable( 64 | ExpressionVariable 65 | ): # pylint: disable=too-few-public-methods 66 | """ 67 | Represents an integer variable defined by an expression. 68 | 69 | The IntegerExpressionVariable class extends the functionality of the 70 | ExpressionVariable class to represent integer variables that are defined by an expression. 71 | 72 | Args: 73 | var_name (str): The name of the variable. 74 | expression: The expression defining the variable. 75 | branch_val (BranchVal, optional): The brancher value strategy. 76 | 77 | Example: 78 | expr = Expression("3 * x + 2") 79 | my_expr_integer_var = IntegerExpressionVariable("y", expression=expr, 80 | branch_val=BranchIntegerVal.VAL_MID) 81 | """ 82 | 83 | def __init__( 84 | self, 85 | var_name: str, 86 | expression: str, 87 | branch_val: BranchVal = BranchIntegerVal.VAL_RND, 88 | ) -> None: 89 | super().__init__(var_name, expression, VariableType.INTEGER, branch_val) 90 | 91 | 92 | class IntegerVariableArray(ArrayVariable): 93 | """ 94 | Represents an array of integer variables. 95 | 96 | The IntegerVariableArray class represents an array of integer variables. 97 | 98 | Args: 99 | var_name (str): The name of the variable. 100 | length (int): The length of the array. 101 | domain_low (int, optional): The lower bound of the variables' domain. 102 | domain_high (int, optional): The upper bound of the variables' domain. 103 | specific_domain (list, optional): A specific domain for the variables. 104 | branch_var (BranchVar, optional): The brancher variable strategy. 105 | branch_val (BranchVal, optional): The brancher value strategy. 106 | 107 | Example: 108 | my_integer_array = IntegerVariableArray("arr", length=5, domain_low=0, domain_high=100, 109 | branch_var=BranchIntegerVar.VAR_MAX) 110 | """ 111 | 112 | def __init__( # pylint: disable=too-many-arguments 113 | self, 114 | var_name: str, 115 | length: int, 116 | domain_low: Optional[int] = None, 117 | domain_high: Optional[int] = None, 118 | specific_domain: Optional[list] = None, 119 | branch_var: BranchVar = BranchIntegerVar.VAR_RND, 120 | branch_val: BranchVal = BranchIntegerVal.VAL_RND, 121 | ) -> None: 122 | super().__init__( 123 | var_name=var_name, 124 | var_type=VariableType.INTEGER_ARRAY, 125 | length=length, 126 | domain_low=domain_low, 127 | domain_high=domain_high, 128 | specific_domain=specific_domain, 129 | branch_var=branch_var, 130 | branch_val=branch_val, 131 | ) 132 | -------------------------------------------------------------------------------- /qaekwy/model/variable/variable.py: -------------------------------------------------------------------------------- 1 | """Variable Module 2 | 3 | This module defines variable bases. 4 | 5 | Enums: 6 | VariableType: Represents different types of variables. 7 | 8 | Classes: 9 | 10 | ArrayVariable: Represents an array-type variable. 11 | Variable: Represents a variable. 12 | ExpressionVariable: Represents a variable defined by an expression. 13 | """ 14 | from enum import Enum 15 | from typing import Optional 16 | 17 | from qaekwy.model.variable.branch import ( 18 | BranchIntegerVal, 19 | BranchIntegerVar, 20 | BranchVal, 21 | BranchVar, 22 | ) 23 | 24 | 25 | class VariableType(Enum): 26 | """ 27 | Represents different types of variables. 28 | 29 | The VariableType enum defines symbolic representations of various variable types 30 | used in constraint-based modeling and optimization, such as integer, integer array, 31 | float, float array, boolean, and boolean array. 32 | 33 | Enum Members: 34 | INTEGER (str): Represents an integer variable. 35 | INTEGER_ARRAY (str): Represents an array of integer variables. 36 | FLOAT (str): Represents a floating-point variable. 37 | FLOAT_ARRAY (str): Represents an array of floating-point variables. 38 | BOOLEAN (str): Represents a boolean variable. 39 | BOOLEAN_ARRAY (str): Represents an array of boolean variables. 40 | 41 | Example: 42 | var_type = VariableType.FLOAT # Represents a floating-point variable 43 | """ 44 | 45 | INTEGER = "integer" 46 | INTEGER_ARRAY = "integer_array" 47 | FLOAT = "float" 48 | FLOAT_ARRAY = "float_array" 49 | BOOLEAN = "boolean" 50 | BOOLEAN_ARRAY = "boolean_array" 51 | 52 | 53 | class Expression: # pylint: disable=missing-class-docstring 54 | def __init__(self, expr): 55 | self.expr = expr 56 | 57 | def __add__(self, expr): 58 | return Expression(f"({self.expr} + {expr})") 59 | 60 | def __radd__(self, expr): 61 | return Expression(f"({expr} + {self.expr})") 62 | 63 | def __sub__(self, expr): 64 | return Expression(f"({self.expr} - {expr})") 65 | 66 | def __rsub__(self, expr): 67 | return Expression(f"({expr} - {self.expr})") 68 | 69 | def __mul__(self, expr): 70 | return Expression(f"{self.expr} * {expr}") 71 | 72 | def __rmul__(self, expr): 73 | return Expression(f"{expr} * {self.expr}") 74 | 75 | def __truediv__(self, expr): 76 | return Expression(f"(({self.expr}) / ({expr}))") 77 | 78 | def __rtruediv__(self, expr): 79 | return Expression(f"(({expr}) / ({self.expr}))") 80 | 81 | def __mod__(self, expr): 82 | return Expression(f"(({self.expr}) % ({expr}))") 83 | 84 | def __rmod__(self, expr): 85 | return Expression(f"(({expr}) % ({self.expr}))") 86 | 87 | def __or__(self, expr): 88 | return Expression(f"({self.expr} | {expr})") 89 | 90 | def __ror__(self, expr): 91 | return Expression(f"({expr} | {self.expr})") 92 | 93 | def __and__(self, expr): 94 | return Expression(f"{self.expr} & {expr}") 95 | 96 | def __rand__(self, expr): 97 | return Expression(f"{expr} & {self.expr}") 98 | 99 | def __eq__(self, expr): 100 | return Expression(f"(({self.expr}) == ({expr}))") 101 | 102 | def __ne__(self, expr): 103 | return Expression(f"(({self.expr}) != ({expr}))") 104 | 105 | def __xor__(self, expr): 106 | return Expression(f"(({self.expr}) ^ ({expr}))") 107 | 108 | def __neg__(self): 109 | return Expression(f"!({self.expr})") 110 | 111 | def __lt__(self, expr): 112 | return Expression(f"(({self.expr}) < ({expr}))") 113 | 114 | def __le__(self, expr): 115 | return Expression(f"(({self.expr}) <= ({expr}))") 116 | 117 | def __gt__(self, expr): 118 | return Expression(f"(({self.expr}) > ({expr}))") 119 | 120 | def __ge__(self, expr): 121 | return Expression(f"(({self.expr}) >= ({expr}))") 122 | 123 | def __str__(self): 124 | return str(self.expr) 125 | 126 | 127 | class ExpressionArray: # pylint: disable=missing-class-docstring 128 | """ 129 | ExpressionArray class represents an array of expressions used for constructing expressions 130 | that involve arrays or table-like structures. 131 | 132 | Args: 133 | array_name (str): The name of the array. 134 | 135 | Methods: 136 | col(table_width: int, column: int) -> Expression: 137 | Creates an Expression for accessing a column in the array-like structure. 138 | 139 | row(table_width: int, row: int) -> Expression: 140 | Creates an Expression for accessing a row in the array-like structure. 141 | 142 | slice() -> Expression: 143 | Creates an Expression for accessing a slice in the array-like structure. 144 | 145 | __getitem__(pos: int) -> Expression: 146 | Overloaded method to create an Expression for accessing a specific 147 | position in the array. 148 | 149 | Example: 150 | col_expression = 151 | my_array.col(table_width=4, column=2) # Creates an expression for column access. 152 | 153 | """ 154 | 155 | def __init__(self, array_name: str) -> None: 156 | """ 157 | Initialize an ExpressionArray instance. 158 | 159 | Args: 160 | array_name (str): The name of the array. 161 | 162 | Returns: 163 | None 164 | """ 165 | self.array_name = array_name 166 | 167 | def col(self, table_width: int, table_height: int, column: int) -> Expression: 168 | """ 169 | Create an Expression for accessing a column in the array-like structure. 170 | 171 | Args: 172 | table_width (int): The width of the table or array-like structure. 173 | table_height (int): The height of the table or array-like structure. 174 | column (int): The index of the column to access. 175 | 176 | Returns: 177 | Expression: An Expression representing the column access. 178 | """ 179 | return Expression( 180 | f"{self.array_name}[{table_width}][{table_height}][c][{column}]" 181 | ) 182 | 183 | def row(self, table_width: int, table_height: int, row: int) -> Expression: 184 | """ 185 | Create an Expression for accessing a row in the array-like structure. 186 | 187 | Args: 188 | table_width (int): The width of the table or array-like structure. 189 | table_height (int): The height of the table or array-like structure. 190 | row (int): The index of the row to access. 191 | 192 | Returns: 193 | Expression: An Expression representing the row access. 194 | """ 195 | return Expression(f"{self.array_name}[{table_width}][{table_height}][r][{row}]") 196 | 197 | def slice( 198 | self, 199 | table_width: int, 200 | table_height: int, 201 | offset_x_start: int, 202 | offset_x_end: int, 203 | offset_y_start: int, 204 | offset_y_end: int, 205 | ) -> Expression: 206 | """ 207 | Create an Expression for accessing a slice in the array-like structure. 208 | 209 | This method generates an Expression that represents a slice operation on an array-like 210 | structure. The slice operation extracts a rectangular subregion from the given array-like 211 | structure, specified by the provided parameters. 212 | 213 | Args: 214 | table_width (int): The width of the array-like structure. 215 | table_height (int): The height of the array-like structure. 216 | offset_x_start (int): The starting index along the x-axis (horizontal) for the slice. 217 | offset_x_end (int): The ending index along the x-axis (horizontal) for the slice. 218 | offset_y_start (int): The starting index along the y-axis (vertical) for the slice. 219 | offset_y_end (int): The ending index along the y-axis (vertical) for the slice. 220 | 221 | Returns: 222 | Expression: An Expression object representing the slice access. 223 | """ 224 | return Expression( 225 | f"{self.array_name}[{table_width}][{table_height}][s][{offset_x_start}][{offset_x_end}][{offset_y_start}][{offset_y_end}]" # pylint: disable=line-too-long 226 | ) 227 | 228 | def __getitem__(self, pos: int) -> Expression: 229 | """ 230 | Create an Expression for accessing a specific position in the array. 231 | 232 | Args: 233 | pos (int): The position index to access. 234 | 235 | Returns: 236 | Expression: An Expression representing the position access. 237 | """ 238 | return Expression(f"{self.array_name}[{pos}]") 239 | 240 | 241 | class ArrayVariable(ExpressionArray): # pylint: disable=too-many-instance-attributes 242 | """ 243 | Represents an array-type variable. 244 | 245 | The ArrayVariable class defines a variable that represents an array of values used in 246 | constraint-based modeling and optimization. 247 | 248 | Args: 249 | var_name (str): The name of the array variable. 250 | length (int): The length of the array. 251 | var_type (VariableType, optional): The type of the array variable. 252 | domain_low (int, optional): The lower bound of the domain for array values. 253 | domain_high (int, optional): The upper bound of the domain for array values. 254 | specific_domain (list, optional): A list of specific values that the array variable. 255 | branch_var (BranchVar, optional): The brancher variable strategy. 256 | branch_val (BranchVal, optional): The brancher value strategy. 257 | 258 | Example: 259 | my_array = 260 | ArrayVariable("my_array", 10, var_type=VariableType.FLOAT_ARRAY, 261 | domain_low=0.0, domain_high=1.0) 262 | """ 263 | 264 | def __init__( # pylint: disable=too-many-arguments 265 | self, 266 | var_name: str, 267 | length: int, 268 | var_type: VariableType = VariableType.INTEGER_ARRAY, 269 | domain_low: Optional[int] = None, 270 | domain_high: Optional[int] = None, 271 | specific_domain: Optional[list] = None, 272 | branch_var: BranchVar = BranchIntegerVar.VAR_RND, 273 | branch_val: BranchVal = BranchIntegerVal.VAL_RND, 274 | ) -> None: 275 | super().__init__(var_name) 276 | self.var_name = var_name 277 | self.var_type = var_type 278 | self.length = length 279 | self.domain_low = domain_low 280 | self.domain_high = domain_high 281 | self.specific_domain = specific_domain 282 | self.branch_var = branch_var 283 | self.branch_val = branch_val 284 | self.branching_order = None 285 | 286 | def set_branching_order(self, branching_order: int): 287 | """ 288 | Sets the branching order. 289 | 290 | Args: 291 | branching_order (int): The branching order value. 292 | 293 | Returns: 294 | None 295 | """ 296 | 297 | self.branching_order = branching_order 298 | 299 | def __len__(self) -> int: 300 | return self.length 301 | 302 | def to_json(self): 303 | """ 304 | Converts the array variable to a JSON representation. 305 | 306 | Returns: 307 | dict: A JSON representation of the array variable. 308 | """ 309 | data_json = { 310 | "name": self.var_name, 311 | "type": self.var_type.value, 312 | "length": self.length, 313 | "brancher_variable": self.branch_var.value, 314 | "brancher_value": self.branch_val.value, 315 | } 316 | 317 | if self.domain_low is not None: 318 | data_json["domlow"] = self.domain_low 319 | 320 | if self.domain_high is not None: 321 | data_json["domup"] = self.domain_high 322 | 323 | if self.specific_domain is not None: 324 | data_json["specific_domain"] = self.specific_domain 325 | 326 | return data_json 327 | 328 | 329 | class Variable(Expression): # pylint: disable=too-few-public-methods 330 | """ 331 | Represents a variable. 332 | 333 | The Variable class defines a variable used in constraint-based modeling and optimization. 334 | It inherits from the Expression class and provides methods to convert the variable to a JSON 335 | representation suitable for serialization. 336 | 337 | Args: 338 | var_name (str): The name of the variable. 339 | domain_low (int, optional): The lower bound of the domain for variable values. 340 | domain_high (int, optional): The upper bound of the domain for variable values. 341 | specific_domain (list, optional): A list of specific values that the variable can take. 342 | var_type (VariableType, optional): The type of the variable. 343 | branch_val (BranchVal, optional): The brancher value strategy. 344 | 345 | Example: 346 | my_variable = Variable("x", domain_low=0, domain_high=10, 347 | specific_domain=[2, 4, 6, 8], var_type=VariableType.INTEGER, 348 | branch_val=BranchIntegerVal.VAL_RND) 349 | """ 350 | 351 | def __init__( # pylint: disable=too-many-arguments 352 | self, 353 | var_name: str, 354 | domain_low: Optional[int] = None, 355 | domain_high: Optional[int] = None, 356 | specific_domain: Optional[list] = None, 357 | var_type: VariableType = VariableType.INTEGER, 358 | branch_val: BranchVal = BranchIntegerVal.VAL_RND, 359 | ) -> None: 360 | super().__init__(var_name) 361 | self.var_name = var_name 362 | self.var_type = var_type 363 | self.domain_low = domain_low 364 | self.domain_high = domain_high 365 | self.specific_domain = specific_domain 366 | self.branch_val = branch_val 367 | self.expression = None 368 | self.branching_order = None 369 | 370 | def set_branching_order(self, branching_order: int): 371 | """ 372 | Sets the branching order. 373 | 374 | Args: 375 | branching_order (int): The branching order value. 376 | 377 | Returns: 378 | None 379 | """ 380 | 381 | self.branching_order = branching_order 382 | 383 | def to_json(self): 384 | """ 385 | Converts the variable to a JSON representation. 386 | 387 | Returns: 388 | dict: A JSON representation of the variable. 389 | """ 390 | 391 | data_json = { 392 | "name": self.var_name, 393 | "type": self.var_type.value, 394 | "brancher_value": self.branch_val.value, 395 | } 396 | 397 | if self.expression is not None: 398 | data_json["expr"] = str(self.expression) 399 | 400 | else: 401 | if self.domain_low is not None: 402 | data_json["domlow"] = self.domain_low 403 | 404 | if self.domain_high is not None: 405 | data_json["domup"] = self.domain_high 406 | 407 | if self.specific_domain is not None: 408 | data_json["specific_domain"] = self.specific_domain 409 | 410 | return data_json 411 | 412 | 413 | class ExpressionVariable(Variable): # pylint: disable=too-few-public-methods 414 | """ 415 | Represents a variable defined by an expression. 416 | 417 | The ExpressionVariable class extends the functionality of the Variable class 418 | to represent variables that are defined by an expression. It inherits from 419 | the Variable class. 420 | 421 | Args: 422 | var_name (str): The name of the variable. 423 | expression: The expression defining the variable. 424 | var_type (VariableType, optional): The type of the variable. 425 | branch_val (BranchVal, optional): The brancher value strategy. 426 | 427 | Example: 428 | expr = Expression(3 * x + 2) 429 | my_expr_variable = ExpressionVariable("y", expression=expr, 430 | var_type=VariableType.FLOAT, 431 | branch_val=BranchIntegerVal.VAL_RND) 432 | """ 433 | 434 | def __init__( 435 | self, 436 | var_name: str, 437 | expression, 438 | var_type: VariableType = VariableType.INTEGER, 439 | branch_val: BranchVal = BranchIntegerVal.VAL_RND, 440 | ) -> None: 441 | super().__init__(var_name, None, None, None, var_type, branch_val) 442 | self.expression = expression 443 | -------------------------------------------------------------------------------- /qaekwy/response.py: -------------------------------------------------------------------------------- 1 | """ 2 | The `qaekwy.response` module provides classes for representing and handling responses 3 | received from the optimization engine. 4 | 5 | This module defines classes that encapsulate different types of responses that can be 6 | received from the optimization engine, such as echo responses, version responses, 7 | status responses, model JSON responses, solution responses, and explanation responses. 8 | 9 | Classes: 10 | AbstractResponse: Base class for defining responses received from the optimization engine. 11 | EchoResponse: Class representing the response received from an 'echo' request. 12 | VersionResponse: Class representing the response received from a 'version' request. 13 | StatusResponse: Class representing the response received from various status-related requests. 14 | ModelJSonResponse: Class representing the response received after submitting a model. 15 | NodeStatus: Represents the status of a cluster node. 16 | SolutionResponse: Class representing the response received containing solution information. 17 | ExplanationResponse: Class representing the response containing explanation information. 18 | 19 | Usage: 20 | # Create an instance of a response class with response content 21 | response = EchoResponse(response_content) 22 | 23 | # Retrieve the status from the response 24 | status = response.get_status() 25 | 26 | # Check if the status is 'Ok' 27 | is_ok = response.is_status_ok() 28 | 29 | # Retrieve the message from the response 30 | message = response.get_message() 31 | 32 | # Retrieve the content from the response 33 | content = response.get_content() 34 | 35 | Note: 36 | The classes provided by this module are designed to encapsulate and facilitate the handling 37 | of various responses received from the optimization engine. They abstract away the details 38 | of response structure, allowing developers to easily access relevant information such as 39 | status, message, and content. 40 | 41 | """ 42 | from abc import ABC 43 | from typing import List 44 | import json 45 | from qaekwy.explanation import Explanation 46 | 47 | from qaekwy.solution import Solution 48 | 49 | 50 | class NodeStatus: # pylint: disable=too-many-instance-attributes, too-few-public-methods 51 | """ 52 | Represents the status of a cluster node. 53 | """ 54 | 55 | def __init__( # pylint: disable=too-many-arguments 56 | self, 57 | identifier: str, 58 | url: str, 59 | is_enabled: bool, 60 | message: str, 61 | is_busy: bool, 62 | number_of_solutions: int, 63 | is_failed: bool, 64 | is_awake, 65 | ) -> None: 66 | self.identifier = identifier 67 | self.url = url 68 | self.is_enabled = is_enabled 69 | self.message = message 70 | self.is_busy = is_busy 71 | self.number_of_solutions = number_of_solutions 72 | self.is_failed = is_failed 73 | self.is_awake = is_awake 74 | 75 | 76 | class AbstractResponse(ABC): 77 | """ 78 | Abstract base class for defining responses received from the optimization engine. 79 | 80 | Attributes: 81 | response_content (dict): The content of the response received from the optimization engine. 82 | 83 | Methods: 84 | get_status(): Retrieve the status from the response. 85 | is_status_ok(): Check if the status is 'Ok'. 86 | get_message(): Retrieve the message from the response. 87 | get_content(): Retrieve the content from the response. 88 | 89 | """ 90 | 91 | def __init__(self, response_content) -> None: 92 | """ 93 | Initialize an AbstractResponse instance. 94 | 95 | Args: 96 | response_content (dict): The content of the response received 97 | from the optimization engine. 98 | """ 99 | super().__init__() 100 | self.response_content = response_content 101 | 102 | def get_status(self) -> str: 103 | """ 104 | Retrieve the status from the response. 105 | 106 | Returns: 107 | str: The status string extracted from the response. 108 | """ 109 | if "status" not in self.response_content: 110 | return "Ok" 111 | return str(self.response_content["status"]) 112 | 113 | def is_status_ok(self) -> bool: 114 | """ 115 | Check if the status is 'Ok'. 116 | 117 | Returns: 118 | bool: True if the status is 'Ok', False otherwise. 119 | """ 120 | return bool(self.get_status() == "Ok") 121 | 122 | def get_message(self) -> str: 123 | """ 124 | Retrieve the message from the response. 125 | 126 | Returns: 127 | str: The message string extracted from the response. 128 | """ 129 | if "message" not in self.response_content: 130 | return "" 131 | return str(self.response_content["message"]) 132 | 133 | def get_content(self) -> any: 134 | """ 135 | Retrieve the content from the response. 136 | 137 | Returns: 138 | any: The content extracted from the response. 139 | """ 140 | if "content" not in self.response_content: 141 | return self.response_content 142 | return self.response_content["content"] 143 | 144 | 145 | class EchoResponse(AbstractResponse): 146 | """ 147 | Represents a response containing an echoed message from the optimization engine. 148 | 149 | Attributes: 150 | response_content (dict): The content of the response received from the optimization engine. 151 | 152 | Methods: 153 | get_status(): Retrieve the status of the response (always returns an empty string). 154 | is_status_ok(): Check if the response status is OK (always returns True). 155 | get_message(): Retrieve the echoed message from the response. 156 | get_content(): Retrieve the content of the response. 157 | 158 | """ 159 | 160 | def get_status(self) -> str: 161 | """ 162 | Retrieve the status of the response. 163 | 164 | Returns: 165 | str: The status of the response (always an empty string). 166 | """ 167 | return "" 168 | 169 | def is_status_ok(self) -> bool: 170 | """ 171 | Check if the response status is OK. 172 | 173 | Returns: 174 | bool: True, indicating that the response status is always OK. 175 | """ 176 | return True 177 | 178 | def get_message(self) -> str: 179 | """ 180 | Retrieve the echoed message from the response. 181 | 182 | Returns: 183 | str: The echoed message extracted from the response. 184 | """ 185 | return str(self.response_content) 186 | 187 | def get_content(self) -> any: 188 | """ 189 | Retrieve the content of the response. 190 | 191 | Returns: 192 | any: The content of the response, which is the echoed message. 193 | """ 194 | return str(self.response_content) 195 | 196 | 197 | class ModelJSonResponse(AbstractResponse): 198 | """ 199 | ModelJSonResponse class represents a response containing a JSON representation of a model. 200 | """ 201 | 202 | 203 | class StatusResponse(AbstractResponse): 204 | """ 205 | Represents a response containing status information from the optimization engine. 206 | 207 | Attributes: 208 | response_content (dict): The content of the response received from the optimization engine. 209 | 210 | Methods: 211 | get_status(): Retrieve the status of the response. 212 | is_status_ok(): Check if the response status is OK. 213 | get_message(): Retrieve the message from the response. 214 | get_type(): Retrieve the type of the status response. 215 | get_code(): Retrieve the code associated with the status response. 216 | is_busy(): Check if the engine is currently busy. 217 | get_number_of_solution_found(): Retrieve the number of solutions found by the engine. 218 | 219 | """ 220 | 221 | def get_type(self) -> str: 222 | """ 223 | Retrieve the type of the status response. 224 | 225 | Returns: 226 | str: The type of the status response, or an empty string if not present in the response. 227 | """ 228 | if "type" in self.response_content: 229 | return str(self.response_content["type"]) 230 | return "" 231 | 232 | def get_code(self) -> int: 233 | """ 234 | Retrieve the code associated with the status response. 235 | 236 | Returns: 237 | int: The code associated with the status response, or -1 if not present in the response. 238 | """ 239 | if "code" in self.response_content: 240 | return int(self.response_content["code"]) 241 | return -1 242 | 243 | def is_busy(self) -> bool: 244 | """ 245 | Check if the engine is currently busy. 246 | 247 | Returns: 248 | bool: True if the engine is busy, False otherwise. 249 | """ 250 | if "busy_node" in self.response_content: 251 | return bool(self.response_content["busy_node"]) 252 | return False 253 | 254 | def get_number_of_solution_found(self) -> int: 255 | """ 256 | Retrieve the number of solutions found by the engine. 257 | 258 | Returns: 259 | int: The number of solutions found, or -1 if not present in the response. 260 | """ 261 | if "current_solution_found" in self.response_content: 262 | return int(self.response_content["current_solution_found"]) 263 | return -1 264 | 265 | 266 | class SolutionResponse(AbstractResponse): 267 | """ 268 | Represents a response containing solutions from the optimization engine. 269 | 270 | Attributes: 271 | response_content (dict): The content of the response received from the optimization engine. 272 | 273 | Methods: 274 | get_status(): Retrieve the status of the response. 275 | is_status_ok(): Check if the response status is OK. 276 | get_message(): Retrieve the message from the response. 277 | get_solutions(): Retrieve a list of solutions provided by the engine. 278 | 279 | """ 280 | 281 | def get_solutions(self) -> List[Solution]: 282 | """ 283 | Retrieve a list of solutions provided by the engine. 284 | 285 | Returns: 286 | List[Solution]: A list of Solution objects representing the solutions. 287 | Returns None if the response status is not OK. 288 | """ 289 | if not self.is_status_ok(): 290 | return None 291 | return [Solution(c) for c in self.get_content()] 292 | 293 | 294 | class ExplanationResponse(AbstractResponse): 295 | """ 296 | Represents a response containing explanations from the optimization engine. 297 | 298 | Attributes: 299 | response_content (dict): The content of the response received from the optimization engine. 300 | 301 | Methods: 302 | get_status(): Retrieve the status of the response. 303 | is_status_ok(): Check if the response status is OK. 304 | get_message(): Retrieve the message from the response. 305 | get_explanation(): Retrieve an Explanation object containing explanations. 306 | 307 | """ 308 | 309 | def get_explanation(self) -> Explanation: 310 | """ 311 | Retrieve an Explanation object containing explanations provided by the engine. 312 | 313 | Returns: 314 | Explanation: An Explanation object representing the explanations. 315 | Returns None if the response status is not OK. 316 | """ 317 | if not self.is_status_ok(): 318 | return None 319 | return Explanation(self.get_content()) 320 | 321 | 322 | class VersionResponse(AbstractResponse): 323 | """ 324 | Represents a response containing version information from the optimization engine. 325 | 326 | Attributes: 327 | response_content (dict): The content of the response received from the optimization engine. 328 | 329 | Methods: 330 | get_status(): Retrieve the status of the response. 331 | is_status_ok(): Check if the response status is OK. 332 | get_message(): Retrieve the message from the response. 333 | get_app(): Retrieve the name of the application. 334 | get_author(): Retrieve the author of the application. 335 | get_version(): Retrieve the version of the application. 336 | get_version_major(): Retrieve the major version number of the application. 337 | get_version_minor(): Retrieve the minor version number of the application. 338 | get_version_build(): Retrieve the build version number of the application. 339 | get_release(): Retrieve the release information of the application. 340 | 341 | """ 342 | 343 | def get_app(self) -> str: 344 | """ 345 | Retrieve the name of the application. 346 | 347 | Returns: 348 | str: The name of the application. 349 | """ 350 | return self.response_content["app"] 351 | 352 | def get_author(self) -> str: 353 | """ 354 | Retrieve the name of the author of the application. 355 | 356 | Returns: 357 | str: The author of the application. 358 | """ 359 | return self.response_content["author"] 360 | 361 | def get_version(self) -> str: 362 | """ 363 | Retrieve the version of the application. 364 | 365 | Returns: 366 | str: The version of the application. 367 | """ 368 | return self.response_content["version"] 369 | 370 | def get_version_major(self) -> int: 371 | """ 372 | Retrieve the major version number of the application. 373 | 374 | Returns: 375 | int: The major version number of the application. 376 | """ 377 | return self.response_content["version_major"] 378 | 379 | def get_version_minor(self) -> int: 380 | """ 381 | Retrieve the minor version number of the application. 382 | 383 | Returns: 384 | int: The minor version number of the application. 385 | """ 386 | return self.response_content["version_minor"] 387 | 388 | def get_version_build(self) -> int: 389 | """ 390 | Retrieve the build version number of the application. 391 | 392 | Returns: 393 | int: The build version number of the application. 394 | """ 395 | return self.response_content["version_build"] 396 | 397 | def get_release(self) -> str: 398 | """ 399 | Retrieve the release information of the application. 400 | 401 | Returns: 402 | str: The release information of the application. 403 | """ 404 | return self.response_content["version_release"] 405 | 406 | 407 | class ClusterStatusResponse: # pylint: disable=too-few-public-methods 408 | """ 409 | Represents a response containing cluster status information from the optimization engine. 410 | 411 | Attributes: 412 | response_content (str): The content of the response received from the optimization engine. 413 | node_status_list (list[NodeStatus]): A list of NodeStatus instances. 414 | 415 | Methods: 416 | get_node_status_list(): Retrieve the list of NodeStatus instances. 417 | 418 | """ 419 | 420 | def __init__(self, response_content): 421 | """ 422 | Initialize a ClusterStatusResponse instance. 423 | 424 | Args: 425 | response_content (str): The content of the response received from 426 | the optimization engine. 427 | """ 428 | node_status_list = [] 429 | j = json.loads(response_content) 430 | 431 | if isinstance(j, list): 432 | for node in j: 433 | node_status_list.append( 434 | NodeStatus( 435 | identifier=node["identifier"], 436 | url=node["url"], 437 | is_enabled=node["enabled"], 438 | message=node["message"], 439 | is_busy=node["busy_node"], 440 | number_of_solutions=node["current_solution_found"], 441 | is_failed=node["failure"], 442 | is_awake=node["awake"], 443 | ) 444 | ) 445 | 446 | self.node_status_list = node_status_list 447 | -------------------------------------------------------------------------------- /qaekwy/solution.py: -------------------------------------------------------------------------------- 1 | """Solution Class 2 | 3 | This module defines the Solution class, which represents a solution to a model. 4 | 5 | Classes: 6 | Solution: Represents a solution to a model. 7 | 8 | """ 9 | 10 | 11 | class Solution(dict): 12 | """ 13 | Represents a solution to a model. 14 | 15 | The Solution class provides a way to represent a solution to a model in the 16 | form of a dictionary. It allows easy access to variable assignments and their 17 | values in the solution. 18 | 19 | Example: 20 | # A Solution instance with solution JSON content 21 | solution_content = [ 22 | {"name": "x", "assigned": True, "value": 5}, 23 | {"name": "y", "assigned": True, "value": 10}, 24 | {"name": "z", "assigned": False, "value": None} 25 | ] 26 | solution = Solution(solution_content) 27 | 28 | # Access variable assignments and their values in the solution 29 | x_value = solution["x"] # Returns 5 30 | y_value = solution["y"] # Returns 10 31 | z_value = solution["z"] # Returns None 32 | 33 | # Or access variable assignments through the Solution attributes: 34 | x_value = solution.x # x_value = 5 35 | y_value = solution.y # y_value = 10 36 | z_value = solution.z # z_value is None 37 | """ 38 | 39 | def __init__(self, solution_json_content: list) -> None: 40 | self.solution_json_content = solution_json_content 41 | 42 | for element in self.solution_json_content: 43 | variable = element["name"] 44 | 45 | if element["assigned"] is True: 46 | val = element["value"] 47 | else: 48 | val = None 49 | 50 | if "position" in element: 51 | position = element["position"] 52 | if variable not in self: 53 | self[variable] = [] 54 | 55 | while len(self[variable]) < position + 1: 56 | self[variable].append(None) 57 | 58 | self[variable][position] = val 59 | 60 | else: 61 | self[variable] = val 62 | 63 | for elem in self.items(): 64 | self.__setattr__(elem[0], elem[1]) 65 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | """ Installation """ 2 | 3 | from setuptools import setup 4 | import qaekwy 5 | 6 | with open("README.md", "r", encoding="UTF-8") as fh: 7 | long_description = fh.read() 8 | 9 | 10 | setup( 11 | name="qaekwy", 12 | version=qaekwy.__version__, 13 | license=qaekwy.__license__, 14 | author=qaekwy.__author__, 15 | author_email=qaekwy.__author_email__, 16 | keywords=[ 17 | "operational research", 18 | "optimization", 19 | "CSP", 20 | "solver", 21 | "constraint", 22 | "constraint programming", 23 | ], 24 | description="Python Client library for Qaekwy Operational Research Solver", 25 | long_description_content_type = "text/markdown", 26 | long_description=long_description, 27 | url="https://qaekwy.io", 28 | classifiers=[ 29 | "Development Status :: 2 - Pre-Alpha", 30 | "Intended Audience :: Developers", 31 | "Intended Audience :: Education", 32 | "Intended Audience :: Science/Research", 33 | "License :: OSI Approved :: European Union Public Licence 1.2 (EUPL 1.2)", 34 | "Topic :: Scientific/Engineering", 35 | "Topic :: Scientific/Engineering :: Artificial Intelligence", 36 | "Topic :: Scientific/Engineering :: Mathematics", 37 | "Topic :: Software Development :: Libraries", 38 | ], 39 | packages=[ 40 | "qaekwy", 41 | "qaekwy.model", 42 | "qaekwy.model.variable", 43 | "qaekwy.model.constraint", 44 | "qaekwy.exception", 45 | ], 46 | project_urls={ 47 | 'Homepage': 'https://qaekwy.io', 48 | 'Documentation': 'https://docs.qaekwy.io', 49 | 'Issues tracker': 'https://github.com/alex-87/qaekwy-python/issues', 50 | 'Github': 'https://github.com/alex-87/qaekwy-python', 51 | }, 52 | ) 53 | -------------------------------------------------------------------------------- /tests/model/constraint/test_abs.py: -------------------------------------------------------------------------------- 1 | # pylint: skip-file 2 | 3 | import unittest 4 | from qaekwy.model.constraint.abs import ConstraintAbs 5 | from qaekwy.model.variable.integer import IntegerVariable 6 | 7 | 8 | class TestConstraintAbs(unittest.TestCase): 9 | 10 | def setUp(self): 11 | self.var1 = IntegerVariable("var1", 0, 10) 12 | self.var2 = IntegerVariable("var2", 0, 10) 13 | 14 | def test_constraint_creation(self): 15 | constraint = ConstraintAbs(self.var1, self.var2, "abs_constraint") 16 | self.assertEqual(constraint.var_1, self.var1) 17 | self.assertEqual(constraint.var_2, self.var2) 18 | self.assertEqual(constraint.constraint_name, "abs_constraint") 19 | 20 | def test_constraint_to_json(self): 21 | constraint = ConstraintAbs(self.var1, self.var2, "abs_constraint") 22 | expected_json = { 23 | "name": "abs_constraint", 24 | "v1": "var1", 25 | "v2": "var2", 26 | "type": "abs" 27 | } 28 | self.assertEqual(constraint.to_json(), expected_json) 29 | 30 | if __name__ == '__main__': 31 | unittest.main() 32 | -------------------------------------------------------------------------------- /tests/model/constraint/test_acos.py: -------------------------------------------------------------------------------- 1 | # pylint: skip-file 2 | 3 | import unittest 4 | from qaekwy.model.constraint.acos import ConstraintACos 5 | from qaekwy.model.variable.float import FloatVariable 6 | 7 | 8 | class TestConstraintACos(unittest.TestCase): 9 | 10 | def setUp(self): 11 | self.var_angle = FloatVariable("var_angle", 0.0, 10.0) 12 | self.var_value = FloatVariable("var_value", 0.0, 10.0) 13 | 14 | def test_constraint_creation(self): 15 | constraint = ConstraintACos(self.var_angle, self.var_value, "acos_constraint") 16 | self.assertEqual(constraint.var_1, self.var_angle) 17 | self.assertEqual(constraint.var_2, self.var_value) 18 | self.assertEqual(constraint.constraint_name, "acos_constraint") 19 | 20 | def test_constraint_to_json(self): 21 | constraint = ConstraintACos(self.var_angle, self.var_value, "acos_constraint") 22 | expected_json = { 23 | "name": "acos_constraint", 24 | "v1": "var_angle", 25 | "v2": "var_value", 26 | "type": "acos" 27 | } 28 | self.assertEqual(constraint.to_json(), expected_json) 29 | 30 | if __name__ == '__main__': 31 | unittest.main() 32 | -------------------------------------------------------------------------------- /tests/model/constraint/test_asin.py: -------------------------------------------------------------------------------- 1 | # pylint: skip-file 2 | 3 | import unittest 4 | from qaekwy.model.constraint.asin import ConstraintASin 5 | from qaekwy.model.variable.float import FloatVariable 6 | 7 | class TestConstraintASin(unittest.TestCase): 8 | 9 | def setUp(self): 10 | self.var_angle = FloatVariable("var_angle", 0.0, 10.0) 11 | self.var_value = FloatVariable("var_value", 0.0, 10.0) 12 | 13 | def test_constraint_creation(self): 14 | constraint = ConstraintASin(self.var_angle, self.var_value, "asin_constraint") 15 | self.assertEqual(constraint.var_1, self.var_angle) 16 | self.assertEqual(constraint.var_2, self.var_value) 17 | self.assertEqual(constraint.constraint_name, "asin_constraint") 18 | 19 | def test_constraint_to_json(self): 20 | constraint = ConstraintASin(self.var_angle, self.var_value, "asin_constraint") 21 | expected_json = { 22 | "name": "asin_constraint", 23 | "v1": "var_angle", 24 | "v2": "var_value", 25 | "type": "asin" 26 | } 27 | self.assertEqual(constraint.to_json(), expected_json) 28 | 29 | if __name__ == '__main__': 30 | unittest.main() 31 | -------------------------------------------------------------------------------- /tests/model/constraint/test_atan.py: -------------------------------------------------------------------------------- 1 | # pylint: skip-file 2 | 3 | import unittest 4 | from qaekwy.model.constraint.atan import ConstraintATan 5 | from qaekwy.model.variable.float import FloatVariable 6 | 7 | class TestConstraintATan(unittest.TestCase): 8 | 9 | def setUp(self): 10 | self.var_angle = FloatVariable("var_angle", 0.0, 10.0) 11 | self.var_value = FloatVariable("var_value", 0.0, 10.0) 12 | 13 | def test_constraint_creation(self): 14 | constraint = ConstraintATan(self.var_angle, self.var_value, "atan_constraint") 15 | self.assertEqual(constraint.var_1, self.var_angle) 16 | self.assertEqual(constraint.var_2, self.var_value) 17 | self.assertEqual(constraint.constraint_name, "atan_constraint") 18 | 19 | def test_constraint_to_json(self): 20 | constraint = ConstraintATan(self.var_angle, self.var_value, "atan_constraint") 21 | expected_json = { 22 | "name": "atan_constraint", 23 | "v1": "var_angle", 24 | "v2": "var_value", 25 | "type": "atan" 26 | } 27 | self.assertEqual(constraint.to_json(), expected_json) 28 | 29 | if __name__ == '__main__': 30 | unittest.main() 31 | -------------------------------------------------------------------------------- /tests/model/constraint/test_cos.py: -------------------------------------------------------------------------------- 1 | # pylint: skip-file 2 | 3 | import unittest 4 | from qaekwy.model.constraint.cos import ConstraintCos 5 | from qaekwy.model.variable.float import FloatVariable 6 | 7 | class TestConstraintCos(unittest.TestCase): 8 | 9 | def setUp(self): 10 | self.var_angle = FloatVariable("var_angle", 0.0, 10.0) 11 | self.var_value = FloatVariable("var_value", 0.0, 10.0) 12 | 13 | def test_constraint_creation(self): 14 | constraint = ConstraintCos(self.var_angle, self.var_value, "cos_constraint") 15 | self.assertEqual(constraint.var_1, self.var_angle) 16 | self.assertEqual(constraint.var_2, self.var_value) 17 | self.assertEqual(constraint.constraint_name, "cos_constraint") 18 | 19 | def test_constraint_to_json(self): 20 | constraint = ConstraintCos(self.var_angle, self.var_value, "cos_constraint") 21 | expected_json = { 22 | "name": "cos_constraint", 23 | "v1": "var_angle", 24 | "v2": "var_value", 25 | "type": "cos" 26 | } 27 | self.assertEqual(constraint.to_json(), expected_json) 28 | 29 | if __name__ == '__main__': 30 | unittest.main() -------------------------------------------------------------------------------- /tests/model/constraint/test_distinct.py: -------------------------------------------------------------------------------- 1 | # pylint: skip-file 2 | 3 | 4 | import unittest 5 | 6 | from qaekwy.model.variable.integer import IntegerVariableArray 7 | from qaekwy.model.constraint.distinct import ConstraintDistinctArray, ConstraintDistinctCol, ConstraintDistinctRow, ConstraintDistinctSlice 8 | 9 | 10 | class TestConstraintDistinctArray(unittest.TestCase): 11 | 12 | def setUp(self): 13 | self.array_var = IntegerVariableArray("array_var", 10, 0, 30) 14 | 15 | def test_constraint_array_creation(self): 16 | constraint = ConstraintDistinctArray(self.array_var, "distinct_array_constraint") 17 | self.assertEqual(constraint.var_1, self.array_var) 18 | self.assertEqual(constraint.constraint_name, "distinct_array_constraint") 19 | 20 | def test_constraint_array_to_json(self): 21 | constraint = ConstraintDistinctArray(self.array_var, "distinct_array_constraint") 22 | expected_json = { 23 | "name": "distinct_array_constraint", 24 | "type": "distinct", 25 | "v1": "array_var", 26 | "selection": "standard" 27 | } 28 | self.assertEqual(constraint.to_json(), expected_json) 29 | 30 | class TestConstraintDistinctRow(unittest.TestCase): 31 | 32 | def setUp(self): 33 | self.array_var = IntegerVariableArray("array_var", 10, 0, 30) 34 | 35 | def test_constraint_row_creation(self): 36 | constraint = ConstraintDistinctRow(self.array_var, size=3, idx=1, constraint_name="distinct_row_constraint") 37 | self.assertEqual(constraint.var_1, self.array_var) 38 | self.assertEqual(constraint.size, 3) 39 | self.assertEqual(constraint.idx, 1) 40 | self.assertEqual(constraint.constraint_name, "distinct_row_constraint") 41 | 42 | def test_constraint_row_to_json(self): 43 | constraint = ConstraintDistinctRow(self.array_var, size=3, idx=1, constraint_name="distinct_row_constraint") 44 | expected_json = { 45 | "name": "distinct_row_constraint", 46 | "type": "distinct", 47 | "v1": "array_var", 48 | "selection": "row", 49 | "size": 3, 50 | "index": 1 51 | } 52 | self.assertEqual(constraint.to_json(), expected_json) 53 | 54 | class TestConstraintDistinctCol(unittest.TestCase): 55 | 56 | def setUp(self): 57 | self.array_var = IntegerVariableArray("array_var", 10, 0, 30) 58 | 59 | def test_constraint_column_creation(self): 60 | constraint = ConstraintDistinctCol(self.array_var, size=3, idx=0, constraint_name="distinct_col_constraint") 61 | self.assertEqual(constraint.var_1, self.array_var) 62 | self.assertEqual(constraint.size, 3) 63 | self.assertEqual(constraint.idx, 0) 64 | self.assertEqual(constraint.constraint_name, "distinct_col_constraint") 65 | 66 | def test_constraint_column_to_json(self): 67 | constraint = ConstraintDistinctCol(self.array_var, size=3, idx=0, constraint_name="distinct_col_constraint") 68 | expected_json = { 69 | "name": "distinct_col_constraint", 70 | "type": "distinct", 71 | "v1": "array_var", 72 | "selection": "col", 73 | "size": 3, 74 | "index": 0 75 | } 76 | self.assertEqual(constraint.to_json(), expected_json) 77 | 78 | class TestConstraintDistinctSlice(unittest.TestCase): 79 | 80 | def setUp(self): 81 | self.array_var = IntegerVariableArray("array_var", 10, 0, 30) 82 | 83 | def test_constraint_slice_creation(self): 84 | constraint = ConstraintDistinctSlice(self.array_var, size=6, offset_start_x=1, offset_start_y=1, offset_end_x=3, offset_end_y=2, constraint_name="distinct_slice_constraint") 85 | self.assertEqual(constraint.var_1, self.array_var) 86 | self.assertEqual(constraint.size, 6) 87 | self.assertEqual(constraint.offset_start_x, 1) 88 | self.assertEqual(constraint.offset_start_y, 1) 89 | self.assertEqual(constraint.offset_end_x, 3) 90 | self.assertEqual(constraint.offset_end_y, 2) 91 | self.assertEqual(constraint.constraint_name, "distinct_slice_constraint") 92 | 93 | def test_constraint_slice_to_json(self): 94 | constraint = ConstraintDistinctSlice(self.array_var, size=6, offset_start_x=1, offset_start_y=1, offset_end_x=3, offset_end_y=2, constraint_name="distinct_slice_constraint") 95 | expected_json = { 96 | "name": "distinct_slice_constraint", 97 | "type": "distinct", 98 | "v1": "array_var", 99 | "selection": "slice", 100 | "size": 6, 101 | "offset_start_x": 1, 102 | "offset_start_y": 1, 103 | "offset_end_x": 3, 104 | "offset_end_y": 2 105 | } 106 | self.assertEqual(constraint.to_json(), expected_json) 107 | 108 | if __name__ == '__main__': 109 | unittest.main() 110 | -------------------------------------------------------------------------------- /tests/model/constraint/test_divide.py: -------------------------------------------------------------------------------- 1 | # pylint: skip-file 2 | 3 | import unittest 4 | from qaekwy.model.constraint.divide import ConstraintDivide 5 | from qaekwy.model.variable.integer import IntegerVariable 6 | 7 | class TestConstraintDivide(unittest.TestCase): 8 | 9 | def setUp(self): 10 | self.numerator = IntegerVariable("numerator", 0, 100) 11 | self.denominator = IntegerVariable("denominator", 1, 10) 12 | self.result = IntegerVariable("result", 1, 10) 13 | 14 | def test_constraint_creation(self): 15 | constraint = ConstraintDivide(self.numerator, self.denominator, self.result, "divide_constraint") 16 | self.assertEqual(constraint.var_1, self.numerator) 17 | self.assertEqual(constraint.var_2, self.denominator) 18 | self.assertEqual(constraint.var_3, self.result) 19 | self.assertEqual(constraint.constraint_name, "divide_constraint") 20 | 21 | def test_constraint_to_json(self): 22 | constraint = ConstraintDivide(self.numerator, self.denominator, self.result, "divide_constraint") 23 | expected_json = { 24 | "name": "divide_constraint", 25 | "v1": "numerator", 26 | "v2": "denominator", 27 | "v3": "result", 28 | "type": "div" 29 | } 30 | self.assertEqual(constraint.to_json(), expected_json) 31 | 32 | if __name__ == '__main__': 33 | unittest.main() 34 | -------------------------------------------------------------------------------- /tests/model/constraint/test_element.py: -------------------------------------------------------------------------------- 1 | # pylint: skip-file 2 | 3 | import unittest 4 | 5 | from qaekwy.model.variable.integer import IntegerVariable, IntegerVariableArray 6 | from qaekwy.model.constraint.element import ConstraintElement 7 | 8 | class TestConstraintElement(unittest.TestCase): 9 | 10 | def setUp(self): 11 | self.mapping_array = IntegerVariable("mapping_array", 3) 12 | self.variable_1 = IntegerVariable("variable_1", 0, 10) 13 | self.variable_2 = IntegerVariable("variable_2", 0, 10) 14 | 15 | def test_constraint_creation(self): 16 | constraint = ConstraintElement(self.mapping_array, self.variable_1, self.variable_2, "element_constraint") 17 | self.assertEqual(constraint.map_array, self.mapping_array) 18 | self.assertEqual(constraint.var_1, self.variable_1) 19 | self.assertEqual(constraint.var_2, self.variable_2) 20 | self.assertEqual(constraint.constraint_name, "element_constraint") 21 | 22 | def test_constraint_to_json(self): 23 | constraint = ConstraintElement(self.mapping_array, self.variable_1, self.variable_2, "element_constraint") 24 | expected_json = { 25 | "name": "element_constraint", 26 | "map": "mapping_array", 27 | "v1": "variable_1", 28 | "v2": "variable_2", 29 | "type": "element" 30 | } 31 | self.assertEqual(constraint.to_json(), expected_json) 32 | 33 | if __name__ == '__main__': 34 | unittest.main() 35 | -------------------------------------------------------------------------------- /tests/model/constraint/test_exponential.py: -------------------------------------------------------------------------------- 1 | # pylint: skip-file 2 | 3 | import unittest 4 | 5 | from qaekwy.model.variable.float import FloatVariable 6 | from qaekwy.model.constraint.exponential import ConstraintExponential 7 | 8 | class TestConstraintExponential(unittest.TestCase): 9 | 10 | def setUp(self): 11 | self.base_variable = FloatVariable("base_variable", 0.0, 10.0) 12 | self.result_variable = FloatVariable("result_variable", 0.0, 10.0) 13 | 14 | def test_constraint_creation(self): 15 | constraint = ConstraintExponential(self.base_variable, self.result_variable, "exponential_constraint") 16 | self.assertEqual(constraint.var_1, self.base_variable) 17 | self.assertEqual(constraint.var_2, self.result_variable) 18 | self.assertEqual(constraint.constraint_name, "exponential_constraint") 19 | 20 | def test_constraint_to_json(self): 21 | constraint = ConstraintExponential(self.base_variable, self.result_variable, "exponential_constraint") 22 | expected_json = { 23 | "name": "exponential_constraint", 24 | "v1": "base_variable", 25 | "v2": "result_variable", 26 | "type": "div" 27 | } 28 | self.assertEqual(constraint.to_json(), expected_json) 29 | 30 | if __name__ == '__main__': 31 | unittest.main() 32 | -------------------------------------------------------------------------------- /tests/model/constraint/test_logarithm.py: -------------------------------------------------------------------------------- 1 | # pylint: skip-file 2 | 3 | import unittest 4 | 5 | from qaekwy.model.variable.float import FloatVariable 6 | from qaekwy.model.constraint.logarithm import ConstraintLogarithme 7 | 8 | 9 | class TestConstraintLogarithme(unittest.TestCase): 10 | 11 | def setUp(self): 12 | self.variable_to_log = FloatVariable("variable_to_log", 0.0, 10.0) 13 | self.result_variable = FloatVariable("result_variable", 0.0, 10.0) 14 | 15 | def test_constraint_creation(self): 16 | constraint = ConstraintLogarithme(self.variable_to_log, self.result_variable, "logarithmic_constraint") 17 | self.assertEqual(constraint.var_1, self.variable_to_log) 18 | self.assertEqual(constraint.var_2, self.result_variable) 19 | self.assertEqual(constraint.constraint_name, "logarithmic_constraint") 20 | 21 | def test_constraint_to_json(self): 22 | constraint = ConstraintLogarithme(self.variable_to_log, self.result_variable, "logarithmic_constraint") 23 | expected_json = { 24 | "name": "logarithmic_constraint", 25 | "v1": "variable_to_log", 26 | "v2": "result_variable", 27 | "type": "log" 28 | } 29 | self.assertEqual(constraint.to_json(), expected_json) 30 | 31 | if __name__ == '__main__': 32 | unittest.main() 33 | -------------------------------------------------------------------------------- /tests/model/constraint/test_maximum.py: -------------------------------------------------------------------------------- 1 | # pylint: skip-file 2 | 3 | import unittest 4 | 5 | from qaekwy.model.variable.integer import IntegerVariable 6 | from qaekwy.model.constraint.maximum import ConstraintMaximum 7 | 8 | class TestConstraintMaximum(unittest.TestCase): 9 | 10 | def setUp(self): 11 | self.variable_1 = IntegerVariable("variable_1", 0, 10) 12 | self.variable_2 = IntegerVariable("variable_2", 0, 10) 13 | self.variable_3 = IntegerVariable("variable_3", 0, 10) 14 | 15 | def test_constraint_creation(self): 16 | constraint = ConstraintMaximum(self.variable_1, self.variable_2, self.variable_3, "max_constraint") 17 | self.assertEqual(constraint.var_1, self.variable_1) 18 | self.assertEqual(constraint.var_2, self.variable_2) 19 | self.assertEqual(constraint.var_2, self.variable_3) 20 | self.assertEqual(constraint.constraint_name, "max_constraint") 21 | 22 | def test_constraint_to_json(self): 23 | constraint = ConstraintMaximum(self.variable_1, self.variable_2, self.variable_3, "max_constraint") 24 | expected_json = { 25 | "name": "max_constraint", 26 | "v1": "variable_1", 27 | "v2": "variable_2", 28 | "v3": "variable_3", 29 | "type": "max" 30 | } 31 | self.assertEqual(constraint.to_json(), expected_json) 32 | 33 | if __name__ == '__main__': 34 | unittest.main() 35 | -------------------------------------------------------------------------------- /tests/model/constraint/test_member.py: -------------------------------------------------------------------------------- 1 | # pylint: skip-file 2 | 3 | import unittest 4 | from qaekwy.model.constraint.member import ConstraintMember, ArrayVariable, Variable 5 | 6 | class TestConstraintMember(unittest.TestCase): 7 | 8 | def setUp(self): 9 | self.array_variable = ArrayVariable("array_variable", length=5) 10 | self.variable_to_check = Variable("variable_to_check") 11 | 12 | def test_constraint_creation(self): 13 | constraint = ConstraintMember(self.array_variable, self.variable_to_check, "member_constraint") 14 | self.assertEqual(constraint.var_1, self.array_variable) 15 | self.assertEqual(constraint.var_2, self.variable_to_check) 16 | self.assertEqual(constraint.constraint_name, "member_constraint") 17 | 18 | def test_constraint_to_json(self): 19 | constraint = ConstraintMember(self.array_variable, self.variable_to_check, "member_constraint") 20 | expected_json = { 21 | "name": "member_constraint", 22 | "v1": "array_variable", 23 | "v2": "variable_to_check", 24 | "type": "member" 25 | } 26 | self.assertEqual(constraint.to_json(), expected_json) 27 | 28 | if __name__ == '__main__': 29 | unittest.main() 30 | -------------------------------------------------------------------------------- /tests/model/constraint/test_minimum.py: -------------------------------------------------------------------------------- 1 | # pylint: skip-file 2 | 3 | import unittest 4 | 5 | from qaekwy.model.variable.integer import IntegerVariable 6 | from qaekwy.model.constraint.minimum import ConstraintMinimum 7 | 8 | class TestConstraintMinimum(unittest.TestCase): 9 | 10 | def setUp(self): 11 | self.variable_1 = IntegerVariable("variable_1", 0, 10) 12 | self.variable_2 = IntegerVariable("variable_2", 0, 10) 13 | self.variable_3 = IntegerVariable("variable_3", 0, 10) 14 | 15 | def test_constraint_creation(self): 16 | constraint = ConstraintMinimum(self.variable_1, self.variable_2, self.variable_3, "min_constraint") 17 | self.assertEqual(constraint.var_1, self.variable_1) 18 | self.assertEqual(constraint.var_2, self.variable_2) 19 | self.assertEqual(constraint.var_3, self.variable_3) 20 | self.assertEqual(constraint.constraint_name, "min_constraint") 21 | 22 | def test_constraint_to_json(self): 23 | constraint = ConstraintMinimum(self.variable_1, self.variable_2, self.variable_3, "min_constraint") 24 | expected_json = { 25 | "name": "min_constraint", 26 | "v1": "variable_1", 27 | "v2": "variable_2", 28 | "v3": "variable_3", 29 | "type": "min" 30 | } 31 | self.assertEqual(constraint.to_json(), expected_json) 32 | 33 | if __name__ == '__main__': 34 | unittest.main() 35 | -------------------------------------------------------------------------------- /tests/model/constraint/test_modulo.py: -------------------------------------------------------------------------------- 1 | # pylint: skip-file 2 | 3 | import unittest 4 | 5 | from qaekwy.model.variable.integer import IntegerVariable 6 | from qaekwy.model.constraint.modulo import ConstraintModulo 7 | 8 | class TestConstraintModulo(unittest.TestCase): 9 | 10 | def setUp(self): 11 | self.dividend_variable = IntegerVariable("dividend_variable", 0, 10) 12 | self.divisor_variable = IntegerVariable("divisor_variable", 0, 10) 13 | self.result_variable = IntegerVariable("result_variable", 0, 10) 14 | 15 | def test_constraint_creation(self): 16 | constraint = ConstraintModulo( 17 | self.dividend_variable, 18 | self.divisor_variable, 19 | self.result_variable, 20 | "modulo_constraint" 21 | ) 22 | self.assertEqual(constraint.var_1, self.dividend_variable) 23 | self.assertEqual(constraint.var_2, self.divisor_variable) 24 | self.assertEqual(constraint.var_3, self.result_variable) 25 | self.assertEqual(constraint.constraint_name, "modulo_constraint") 26 | 27 | def test_constraint_to_json(self): 28 | constraint = ConstraintModulo( 29 | self.dividend_variable, 30 | self.divisor_variable, 31 | self.result_variable, 32 | "modulo_constraint" 33 | ) 34 | expected_json = { 35 | "name": "modulo_constraint", 36 | "v1": "dividend_variable", 37 | "v2": "divisor_variable", 38 | "v3": "result_variable", 39 | "type": "mod" 40 | } 41 | self.assertEqual(constraint.to_json(), expected_json) 42 | 43 | if __name__ == '__main__': 44 | unittest.main() 45 | -------------------------------------------------------------------------------- /tests/model/constraint/test_multiply.py: -------------------------------------------------------------------------------- 1 | # pylint: skip-file 2 | 3 | import unittest 4 | 5 | from qaekwy.model.variable.integer import IntegerVariable 6 | from qaekwy.model.constraint.multiply import ConstraintMultiply 7 | 8 | class TestConstraintMultiply(unittest.TestCase): 9 | 10 | def setUp(self): 11 | self.variable_1 = IntegerVariable("variable_1", 0, 10) 12 | self.variable_2 = IntegerVariable("variable_2", 0, 10) 13 | self.result_variable = IntegerVariable("result_variable", 0, 10) 14 | 15 | def test_constraint_creation(self): 16 | constraint = ConstraintMultiply( 17 | self.variable_1, 18 | self.variable_2, 19 | self.result_variable, 20 | "multiply_constraint" 21 | ) 22 | self.assertEqual(constraint.var_1, self.variable_1) 23 | self.assertEqual(constraint.var_2, self.variable_2) 24 | self.assertEqual(constraint.var_3, self.result_variable) 25 | self.assertEqual(constraint.constraint_name, "multiply_constraint") 26 | 27 | def test_constraint_to_json(self): 28 | constraint = ConstraintMultiply( 29 | self.variable_1, 30 | self.variable_2, 31 | self.result_variable, 32 | "multiply_constraint" 33 | ) 34 | expected_json = { 35 | "name": "multiply_constraint", 36 | "v1": "variable_1", 37 | "v2": "variable_2", 38 | "v3": "result_variable", 39 | "type": "mul" 40 | } 41 | self.assertEqual(constraint.to_json(), expected_json) 42 | 43 | if __name__ == '__main__': 44 | unittest.main() 45 | -------------------------------------------------------------------------------- /tests/model/constraint/test_nroot.py: -------------------------------------------------------------------------------- 1 | # pylint: skip-file 2 | 3 | import unittest 4 | 5 | from qaekwy.model.variable.integer import IntegerVariable 6 | from qaekwy.model.constraint.nroot import ConstraintNRoot 7 | 8 | class TestConstraintNRoot(unittest.TestCase): 9 | 10 | def setUp(self): 11 | self.variable_to_root = IntegerVariable("variable_to_root", 0, 10) 12 | self.n_value = 3 13 | self.result_variable = IntegerVariable("result_variable", 0, 10) 14 | 15 | def test_constraint_creation(self): 16 | constraint = ConstraintNRoot( 17 | self.variable_to_root, 18 | self.n_value, 19 | self.result_variable, 20 | "nroot_constraint" 21 | ) 22 | self.assertEqual(constraint.var_1, self.variable_to_root) 23 | self.assertEqual(constraint.var_2, self.n_value) 24 | self.assertEqual(constraint.var_3, self.result_variable) 25 | self.assertEqual(constraint.constraint_name, "nroot_constraint") 26 | 27 | def test_constraint_to_json(self): 28 | constraint = ConstraintNRoot( 29 | self.variable_to_root, 30 | self.n_value, 31 | self.result_variable, 32 | "nroot_constraint" 33 | ) 34 | expected_json = { 35 | "name": "nroot_constraint", 36 | "v1": "variable_to_root", 37 | "v2": 3, 38 | "v3": "result_variable", 39 | "type": "nroot" 40 | } 41 | self.assertEqual(constraint.to_json(), expected_json) 42 | 43 | if __name__ == '__main__': 44 | unittest.main() 45 | -------------------------------------------------------------------------------- /tests/model/constraint/test_power.py: -------------------------------------------------------------------------------- 1 | # pylint: skip-file 2 | 3 | import unittest 4 | 5 | from qaekwy.model.variable.integer import IntegerVariable 6 | from qaekwy.model.constraint.power import ConstraintPower 7 | 8 | class TestConstraintPower(unittest.TestCase): 9 | 10 | def setUp(self): 11 | self.base_variable = IntegerVariable("base_variable", 0, 10) 12 | self.exponent_value = 2 13 | self.result_variable = IntegerVariable("result_variable", 0, 10) 14 | 15 | def test_constraint_creation(self): 16 | constraint = ConstraintPower( 17 | self.base_variable, 18 | self.exponent_value, 19 | self.result_variable, 20 | "power_constraint" 21 | ) 22 | self.assertEqual(constraint.var_1, self.base_variable) 23 | self.assertEqual(constraint.var_2, self.exponent_value) 24 | self.assertEqual(constraint.var_3, self.result_variable) 25 | self.assertEqual(constraint.constraint_name, "power_constraint") 26 | 27 | def test_constraint_to_json(self): 28 | constraint = ConstraintPower( 29 | self.base_variable, 30 | self.exponent_value, 31 | self.result_variable, 32 | "power_constraint" 33 | ) 34 | expected_json = { 35 | "name": "power_constraint", 36 | "v1": "base_variable", 37 | "v2": 2, 38 | "v3": "result_variable", 39 | "type": "pow" 40 | } 41 | self.assertEqual(constraint.to_json(), expected_json) 42 | 43 | if __name__ == '__main__': 44 | unittest.main() 45 | -------------------------------------------------------------------------------- /tests/model/constraint/test_relational.py: -------------------------------------------------------------------------------- 1 | # pylint: skip-file 2 | 3 | 4 | import unittest 5 | 6 | from qaekwy.model.variable.variable import Expression 7 | from qaekwy.model.variable.integer import IntegerVariable 8 | 9 | from qaekwy.model.constraint.relational import RelationalExpression 10 | 11 | 12 | class TestRelationalExpression(unittest.TestCase): 13 | 14 | def setUp(self): 15 | self.var_1 = IntegerVariable("var_1", 0, 10) 16 | self.var_2 = IntegerVariable("var_2", 0, 10) 17 | self.var_3 = IntegerVariable("var_3", 0, 10) 18 | self.expression = Expression(self.var_1 + self.var_2 >= self.var_3 + 1) 19 | 20 | def test_constraint_creation(self): 21 | constraint = RelationalExpression(self.expression, "relational_constraint") 22 | self.assertEqual(constraint.expr, self.expression) 23 | self.assertEqual(constraint.constraint_name, "relational_constraint") 24 | 25 | def test_constraint_to_json(self): 26 | constraint = RelationalExpression(self.expression, "relational_constraint") 27 | expected_json = { 28 | "name": "relational_constraint", 29 | "expr": "(((var_1 + var_2)) >= ((var_3 + 1)))", 30 | "type": "rel" 31 | } 32 | self.assertEqual(constraint.to_json(), expected_json) 33 | 34 | if __name__ == '__main__': 35 | unittest.main() 36 | -------------------------------------------------------------------------------- /tests/model/constraint/test_sin.py: -------------------------------------------------------------------------------- 1 | # pylint: skip-file 2 | 3 | import unittest 4 | 5 | from qaekwy.model.variable.float import FloatVariable 6 | from qaekwy.model.constraint.sin import ConstraintSin 7 | 8 | class TestConstraintSin(unittest.TestCase): 9 | 10 | def test_constraint_creation(self): 11 | var_1 = FloatVariable("var_1", 0.0, 10.0) 12 | var_2 = FloatVariable("var_2", 0.0, 10.0) 13 | constraint = ConstraintSin(var_1, var_2, "sine_constraint") 14 | 15 | self.assertEqual(constraint.var_1, var_1) 16 | self.assertEqual(constraint.var_2, var_2) 17 | self.assertEqual(constraint.constraint_name, "sine_constraint") 18 | 19 | def test_constraint_to_json(self): 20 | var_1 = FloatVariable("var_1") 21 | var_2 = FloatVariable("var_2") 22 | constraint = ConstraintSin(var_1, var_2, "sine_constraint") 23 | 24 | expected_json = { 25 | "name": "sine_constraint", 26 | "v1": "var_1", 27 | "v2": "var_2", 28 | "type": "sin" 29 | } 30 | 31 | self.assertEqual(constraint.to_json(), expected_json) 32 | 33 | if __name__ == '__main__': 34 | unittest.main() 35 | -------------------------------------------------------------------------------- /tests/model/constraint/test_sort.py: -------------------------------------------------------------------------------- 1 | # pylint: skip-file 2 | 3 | import unittest 4 | 5 | from qaekwy.model.variable.integer import IntegerVariableArray 6 | from qaekwy.model.constraint.sort import ConstraintSorted, ConstraintReverseSorted 7 | 8 | 9 | class TestConstraintSorted(unittest.TestCase): 10 | 11 | def test_sorted_constraint_creation(self): 12 | array_var = IntegerVariableArray("array_var", 10, 0, 200) 13 | sorted_constraint = ConstraintSorted(array_var, "sorted_constraint") 14 | 15 | self.assertEqual(sorted_constraint.var_1, array_var) 16 | self.assertEqual(sorted_constraint.constraint_name, "sorted_constraint") 17 | 18 | def test_sorted_constraint_to_json(self): 19 | array_var = IntegerVariableArray("array_var", 10, 0, 200) 20 | sorted_constraint = ConstraintSorted(array_var, "sorted_constraint") 21 | 22 | expected_json = { 23 | "name": "sorted_constraint", 24 | "v1": "array_var", 25 | "type": "sorted" 26 | } 27 | 28 | self.assertEqual(sorted_constraint.to_json(), expected_json) 29 | 30 | 31 | class TestConstraintReverseSorted(unittest.TestCase): 32 | 33 | def test_reverse_sorted_constraint_creation(self): 34 | array_var = IntegerVariableArray("array_var", 10, 0, 200) 35 | reverse_sorted_constraint = ConstraintReverseSorted(array_var, "reverse_sorted_constraint") 36 | 37 | self.assertEqual(reverse_sorted_constraint.var_1, array_var) 38 | self.assertEqual(reverse_sorted_constraint.constraint_name, "reverse_sorted_constraint") 39 | 40 | def test_reverse_sorted_constraint_to_json(self): 41 | array_var = IntegerVariableArray("array_var", 10, 0, 200) 42 | reverse_sorted_constraint = ConstraintReverseSorted(array_var, "reverse_sorted_constraint") 43 | 44 | expected_json = { 45 | "name": "reverse_sorted_constraint", 46 | "v1": "array_var", 47 | "type": "rsorted" 48 | } 49 | 50 | self.assertEqual(reverse_sorted_constraint.to_json(), expected_json) 51 | 52 | if __name__ == '__main__': 53 | unittest.main() 54 | -------------------------------------------------------------------------------- /tests/model/constraint/test_tan.py: -------------------------------------------------------------------------------- 1 | # pylint: skip-file 2 | 3 | import unittest 4 | 5 | from qaekwy.model.variable.float import FloatVariable 6 | from qaekwy.model.constraint.tan import ConstraintTan 7 | 8 | 9 | class TestConstraintTan(unittest.TestCase): 10 | 11 | def test_tan_constraint_creation(self): 12 | var_1 = FloatVariable("var_1", 0.0, 10.0) 13 | var_2 = FloatVariable("var_2", 0.0, 10.0) 14 | tan_constraint = ConstraintTan(var_1, var_2, "tan_constraint") 15 | 16 | self.assertEqual(tan_constraint.var_1, var_1) 17 | self.assertEqual(tan_constraint.var_2, var_2) 18 | self.assertEqual(tan_constraint.constraint_name, "tan_constraint") 19 | 20 | def test_tan_constraint_to_json(self): 21 | var_1 = FloatVariable("var_1", 0.0, 10.0) 22 | var_2 = FloatVariable("var_2", 0.0, 10.0) 23 | tan_constraint = ConstraintTan(var_1, var_2, "tan_constraint") 24 | 25 | expected_json = { 26 | "name": "tan_constraint", 27 | "v1": "var_1", 28 | "v2": "var_2", 29 | "type": "tan" 30 | } 31 | 32 | self.assertEqual(tan_constraint.to_json(), expected_json) 33 | 34 | if __name__ == '__main__': 35 | unittest.main() 36 | -------------------------------------------------------------------------------- /tests/model/test_modeller.py: -------------------------------------------------------------------------------- 1 | # pylint: skip-file 2 | 3 | import unittest 4 | from qaekwy.model.constraint.abs import ConstraintAbs 5 | from qaekwy.model.modeller import Modeller 6 | from qaekwy.model.specific import SpecificMinimum 7 | from qaekwy.model.searcher import SearcherType 8 | from qaekwy.model.cutoff import CutoffFibonacci 9 | from qaekwy.model.variable.integer import IntegerVariable 10 | 11 | class TestModeller(unittest.TestCase): 12 | 13 | def setUp(self): 14 | self.modeller = Modeller() 15 | self.var1 = IntegerVariable("var1", 0, 10) 16 | self.var2 = IntegerVariable("var2", 0, 10) 17 | self.constraint = ConstraintAbs(var_1=self.var1, var_2=self.var2, constraint_name="abs") 18 | self.objective = SpecificMinimum(self.var1) 19 | self.searcher = SearcherType.DFS 20 | self.cutoff = CutoffFibonacci() 21 | self.callback_url = "https://example.com/callback" 22 | 23 | def test_add_variable(self): 24 | self.modeller.add_variable(self.var1) 25 | self.assertEqual(self.modeller.variable_list, [self.var1]) 26 | 27 | def test_add_constraint(self): 28 | self.modeller.add_constraint(self.constraint) 29 | self.assertEqual(self.modeller.constraint_list, [self.constraint]) 30 | 31 | def test_add_objective(self): 32 | self.modeller.add_objective(self.objective) 33 | self.assertEqual(self.modeller.objective_list, [self.objective]) 34 | 35 | def test_set_searcher(self): 36 | self.modeller.set_searcher(self.searcher) 37 | self.assertEqual(self.modeller.searcher, self.searcher) 38 | 39 | def test_set_cutoff(self): 40 | self.modeller.set_cutoff(self.cutoff) 41 | self.assertEqual(self.modeller.cutoff, self.cutoff) 42 | 43 | def test_set_callback_url(self): 44 | self.modeller.set_callback_url(self.callback_url) 45 | self.assertEqual(self.modeller.callback_url, self.callback_url) 46 | 47 | def test_to_json(self): 48 | self.modeller.add_variable(self.var1).add_constraint(self.constraint).add_objective(self.objective) 49 | self.modeller.set_searcher(self.searcher).set_cutoff(self.cutoff).set_callback_url(self.callback_url) 50 | 51 | expected_json = { 52 | 'callback_url': 'https://example.com/callback', 53 | 'constraint': [ 54 | {'name': 'abs', 'type': 'abs', 'v1': 'var1', 'v2': 'var2'} 55 | ], 56 | 'cutoff': {'name': 'fibonacci'}, 57 | 'searcher': 'DFS', 58 | 'solution_limit': 1, 59 | 'specific': [{'type': 'minimize', 'var': 'var1'}], 60 | 'var': [ 61 | {'brancher_value': 'VAL_RND', 'domlow': 0, 'domup': 10, 'name': 'var1', 'type': 'integer'} 62 | ] 63 | } 64 | 65 | self.assertEqual(self.modeller.to_json(), expected_json) 66 | 67 | if __name__ == '__main__': 68 | unittest.main() 69 | -------------------------------------------------------------------------------- /tests/model/variable/test_expression.py: -------------------------------------------------------------------------------- 1 | # pylint: skip-file 2 | 3 | import unittest 4 | from qaekwy.model.variable.variable import Expression 5 | 6 | class TestExpression(unittest.TestCase): 7 | 8 | def test_arithmetic_operators(self): 9 | expr = Expression(2) 10 | 11 | self.assertEqual(str(expr + 3), "(2 + 3)") 12 | self.assertEqual(str(4 + expr), "(4 + 2)") 13 | 14 | self.assertEqual(str(expr - 3), "(2 - 3)") 15 | self.assertEqual(str(4 - expr), "(4 - 2)") 16 | 17 | self.assertEqual(str(expr * 3), "2 * 3") 18 | self.assertEqual(str(4 * expr), "4 * 2") 19 | 20 | self.assertEqual(str(expr / 3), "((2) / (3))") 21 | self.assertEqual(str(4 / expr), "((4) / (2))") 22 | 23 | self.assertEqual(str(expr % 3), "((2) % (3))") 24 | self.assertEqual(str(4 % expr), "((4) % (2))") 25 | 26 | def test_logical_operators(self): 27 | expr1 = Expression(True) 28 | expr2 = Expression(False) 29 | 30 | self.assertEqual(str(expr1 & expr2), "True & False") 31 | self.assertEqual(str(expr1 | expr2), "(True | False)") 32 | self.assertEqual(str(expr1 ^ expr2), "((True) ^ (False))") 33 | 34 | def test_relational_operators(self): 35 | expr = Expression(5) 36 | 37 | self.assertEqual(str(expr == 5), "((5) == (5))") 38 | self.assertEqual(str(expr != 5), "((5) != (5))") 39 | self.assertEqual(str(expr < 6), "((5) < (6))") 40 | self.assertEqual(str(expr <= 6), "((5) <= (6))") 41 | self.assertEqual(str(expr > 4), "((5) > (4))") 42 | self.assertEqual(str(expr >= 4), "((5) >= (4))") 43 | 44 | if __name__ == '__main__': 45 | unittest.main() 46 | -------------------------------------------------------------------------------- /tests/model/variable/test_integer.py: -------------------------------------------------------------------------------- 1 | # pylint: skip-file 2 | 3 | import unittest 4 | 5 | from qaekwy.model.variable.integer import IntegerVariable 6 | from qaekwy.model.variable.variable import Expression 7 | 8 | class TestExpression(unittest.TestCase): 9 | def test_arithmetic_operations(self): 10 | expr = Expression("x") 11 | expr_add = expr + 2 12 | self.assertEqual(str(expr_add), "(x + 2)") 13 | 14 | expr_sub = expr - 3 15 | self.assertEqual(str(expr_sub), "(x - 3)") 16 | 17 | expr_mul = expr * 4 18 | self.assertEqual(str(expr_mul), "x * 4") 19 | 20 | expr_div = expr / 5 21 | self.assertEqual(str(expr_div), "((x) / (5))") 22 | 23 | expr_mod = expr % 6 24 | self.assertEqual(str(expr_mod), "((x) % (6))") 25 | 26 | class TestVariable(unittest.TestCase): 27 | def test_variable_to_json(self): 28 | var = IntegerVariable("x", domain_low=0, domain_high=10) 29 | 30 | var_json = var.to_json() 31 | self.assertEqual(var_json["name"], "x") 32 | self.assertEqual(var_json["type"], "integer") 33 | self.assertEqual(var_json["brancher_value"], "VAL_RND") 34 | self.assertEqual(var_json["domlow"], 0) 35 | self.assertEqual(var_json["domup"], 10) 36 | 37 | 38 | class TestSpecificDomainVariable(unittest.TestCase): 39 | def test_variable_to_json(self): 40 | var = IntegerVariable("x", specific_domain=[2, 4, 6]) 41 | 42 | var_json = var.to_json() 43 | self.assertEqual(var_json["name"], "x") 44 | self.assertEqual(var_json["type"], "integer") 45 | self.assertEqual(var_json["brancher_value"], "VAL_RND") 46 | self.assertEqual(var_json["specific_domain"], [2, 4, 6]) 47 | 48 | 49 | class TestExprVariable(unittest.TestCase): 50 | def test_variable_to_json(self): 51 | var = IntegerVariable("x") 52 | var_expr = var + 2 53 | var.expression = var_expr 54 | 55 | var_json = var.to_json() 56 | self.assertEqual(var_json["name"], "x") 57 | self.assertEqual(var_json["type"], "integer") 58 | self.assertEqual(var_json["brancher_value"], "VAL_RND") 59 | self.assertEqual(var_json["expr"], "(x + 2)") 60 | 61 | 62 | class TestIntegerVariable(unittest.TestCase): 63 | def test_integer_variable_to_json(self): 64 | int_var = IntegerVariable("y", domain_low=1, domain_high=5) 65 | int_var_json = int_var.to_json() 66 | self.assertEqual(int_var_json["name"], "y") 67 | self.assertEqual(int_var_json["type"], "integer") 68 | self.assertEqual(int_var_json["brancher_value"], "VAL_RND") 69 | self.assertEqual(int_var_json["domlow"], 1) 70 | self.assertEqual(int_var_json["domup"], 5) 71 | 72 | 73 | if __name__ == "__main__": 74 | unittest.main() 75 | -------------------------------------------------------------------------------- /tests/test_explanation.py: -------------------------------------------------------------------------------- 1 | # pylint: skip-file 2 | 3 | import unittest 4 | 5 | from qaekwy.explanation import Explanation 6 | 7 | class ExplanationTest(unittest.TestCase): 8 | 9 | def test_init(self): 10 | explanation_content = [ 11 | {"name": "x", "type": "var", "explanation": "x is a variable"}, 12 | {"name": "y", "type": "constraint", "explanation": "y is a constraint"}, 13 | ] 14 | explanation = Explanation(explanation_content) 15 | 16 | self.assertEqual(explanation.get_variables()["x"]["type"], "var") 17 | self.assertEqual(explanation.get_variables()["x"]["explanation"], "x is a variable") 18 | self.assertEqual(explanation.get_constraints()["y"]["type"], "constraint") 19 | self.assertEqual(explanation.get_constraints()["y"]["explanation"], "y is a constraint") 20 | 21 | def test_missing_variable(self): 22 | explanation_content = [ 23 | {"name": "y", "type": "constraint", "explanation": "y is a constraint"}, 24 | ] 25 | explanation = Explanation(explanation_content) 26 | 27 | with self.assertRaises(KeyError): 28 | explanation.get_variables()["x"] 29 | 30 | def test_missing_constraint(self): 31 | explanation_content = [ 32 | {"name": "x", "type": "var", "explanation": "x is a variable"}, 33 | ] 34 | explanation = Explanation(explanation_content) 35 | 36 | with self.assertRaises(KeyError): 37 | explanation.get_constraints()["y"] 38 | 39 | if __name__ == "__main__": 40 | unittest.main() 41 | -------------------------------------------------------------------------------- /tests/test_solution.py: -------------------------------------------------------------------------------- 1 | # pylint: skip-file 2 | 3 | import unittest 4 | 5 | from qaekwy.solution import Solution 6 | 7 | class SolutionTest(unittest.TestCase): 8 | 9 | def test_init(self): 10 | solution_json_content = [ 11 | {"name": "x", "assigned": True, "value": 5}, 12 | {"name": "y", "assigned": True, "value": 10}, 13 | {"name": "z", "assigned": False, "value": None} 14 | ] 15 | solution = Solution(solution_json_content) 16 | 17 | self.assertEqual(solution["x"], 5) 18 | self.assertEqual(solution["y"], 10) 19 | self.assertEqual(solution["z"], None) 20 | 21 | self.assertTrue(hasattr(solution, "x")) 22 | self.assertTrue(hasattr(solution, "y")) 23 | self.assertTrue(hasattr(solution, "z")) 24 | 25 | self.assertEqual(solution.x, 5) 26 | self.assertEqual(solution.y, 10) 27 | self.assertEqual(solution.z, None) 28 | 29 | def test_positional_assignment(self): 30 | solution_json_content = [ 31 | {"name": "x", "assigned": True, "value": 5, "position": 1}, 32 | {"name": "y", "assigned": True, "value": 10, "position": 0}, 33 | {"name": "z", "assigned": False, "value": None} 34 | ] 35 | solution = Solution(solution_json_content) 36 | 37 | self.assertEqual(solution["x"], [None, 5]) 38 | self.assertEqual(solution["y"][0], 10) 39 | self.assertEqual(solution["z"], None) 40 | 41 | def test_missing_variable(self): 42 | solution_json_content = [ 43 | {"name": "x", "assigned": True, "value": 5}, 44 | {"name": "y", "assigned": True, "value": 10}, 45 | ] 46 | solution = Solution(solution_json_content) 47 | 48 | with self.assertRaises(KeyError): 49 | solution["z"] 50 | 51 | def test_invalid_position(self): 52 | solution_json_content = [ 53 | {"name": "x", "assigned": True, "value": 5, "position": 1}, 54 | {"name": "y", "assigned": True, "value": 10, "position": 0}, 55 | {"name": "z", "assigned": False, "value": None} 56 | ] 57 | solution = Solution(solution_json_content) 58 | 59 | with self.assertRaises(IndexError): 60 | solution["x"][2] 61 | 62 | if __name__ == "__main__": 63 | unittest.main() 64 | --------------------------------------------------------------------------------