The answer by Mark L. Stone is to the point. In addition to it, I would suggest you read about metaheuristics, especially Genetic Algorithms. Some useful links for it:
 Goldberg, D. E. (2006). Genetic algorithms. Pearson Education India.
 Bäck, T., Fogel, D. B., & Michalewicz, Z. (1997). Handbook of evolutionary computation. CRC Press.
The GAs are population-based randomized search heuristics, inspired by the theory of genetics and natural selection, whose search is not strictly dependent on the nature (linear/non-linear) of the optimization problem. One thing I have experienced about GAs is that the GA-search can be as powerful/efficient as you could design it by customizing operators using the problem's domain knowledge.
For its code in python, you could refer to the GA-code uploaded by Prof. K. Deb:
 Blank, J., & Deb, K. (2020). pymoo: Multi-objective Optimization in Python. arXiv preprint arXiv:2002.04504.
Examples of customized operators in GA are:
 enhanced initialization, i.e., to generate at least one feasible solution in the initial population instead of all randomized ones,
 retaining poor/infeasible solutions to maintain diversity in the population,
 enhanced crossover/mutation operators,
 infeasibility repair operators, etc.
Some useful articles for such customizations are (though the problem formulations might be different, you will get some idea about it):
 J. E. Beasley, P. C. Chu, A genetic algorithm for the set covering problem, European journal of operational research 94 (2) (1996) 392–404.
 Deb, K., & Myburgh, C. (2016, July). Breaking the billion-variable barrier in real-world optimization using a customized evolutionary algorithm. In Proceedings of the Genetic and Evolutionary Computation Conference 2016 (pp. 653-660).
 D. Aggarwal, D. K. Saxena, T. Bäck, M. Emmerich, Real-World Airline Crew Pairing Optimization: Customized Genetic Algorithm versus Column Generation Method, arXiv:2003.03792 [cs.NE] (Unpublished).