Artificial Coronary Circulation Algorithm (ACCS)
Contents
Introduction
The Artificial Coronary Circulation System (ACCS) algorithm is a bio-inspired metaheuristic optimization method. ACCS mimics the growth of coronary arteries in the human heart. The idea is that each artery or capillary represents a candidate solution, and the entire process of vascular system growth is analogous to searching for an optimum in a complex solution space. The algorithm was proposed by A. Kaveh and M. Kooshkbaghi in 2019 in the paper Artificial coronary circulation system: A new bio-inspired metaheuristic algorithm.
Implementation of the algorithm
Imagine that your heart is not just a pump — it is a wise architect that builds perfect pathways for blood. Every second, it solves an extremely complex problem: how to deliver as much oxygen as possible while using as little energy as possible. Scientists took inspiration from this ingenious strategy and decided to use it as the basis for the ACCS algorithm.
At the beginning, there is chaos. Just as in nature, when capillaries are only beginning to sprout, ACCS generates a random population of solutions. A capillary leader (CL), which begins searching for a path to the optimal solution, is evaluated using the Coronary Growth Factor (CGF) — like an energy level that indicates how well it is performing the task. Each capillary chooses how to grow: search — it stretches forward like a root, hoping to find fertile soil; branching — if conditions are good, it creates new branches, like a tree sending out shoots; and pruning — if growth is useless, it stops to avoid wasting resources.
Heart Memory. To keep track of which branches were the strongest, ACCS uses Heart Memory (HM) — a record of the best solutions are stored. These entries serve as a guide for growth, helping new capillaries grow in the right direction.
Self-learning and adaptation. If a capillary grows in the wrong direction, the algorithm activates a local search — it starts probing nearby, like a root searching for water. If a capillary suddenly turns out to be the best, a global search is launched, as if the tree had decided to grow in a new direction.
ACCS is not just dry math — it is the art of learning from nature. Your heart has been solving the most complex optimization problems throughout your life, and now algorithms can draw on that same wisdom: explore in diverse ways (like the coronary arteries), remember what is best (like Heart Memory), let go of failures (like vascular pruning), and balance global search with local refinement.
This algorithm reminds us that the best solutions often already exist in nature. All it takes is the ability to notice them and learn from the wisdom that beats in every one of our chests. Let’s see what this looks like in the illustration.

Figure 1. Coronary system
The image shows the main elements: a schematic representation of the heart with its coronary arteries. The left coronary artery (LCA) with its branches, LAD and Circumflex; the right coronary artery (RCA); small capillaries demonstrating local search; and a bifurcation pattern (branching) in the upper right.
Algorithm concepts. The lower section shows how biological elements correspond to the algorithm’s components:
Major arteries → Global search
Bifurcation → Branching factor
Capillaries → Local search
Blood flow → CGF (Coronary Growth Factor)
This visual representation helps illustrate how the heart’s natural blood supply system inspired the creation of an optimization algorithm, in which “arteries” explore the solution space and “capillaries” fine-tune the solutions found. Let’s write the pseudocode for the algorithm.
-
Initialization
-
Set the parameters: population size (popSize), Heart Memory size (heartMemorySize)
-
Create an initial population of capillaries (agents) with random positions in the solution space
-
Initialize Heart Memory to store the best solutions
-
-
Main loop (until the stopping criterion is met)
-
Calculate the Coronary Growth Factor (CGF) for each agent based on their fitness values
-
Perform a global search (artery growth) — update agent positions by moving toward or away from the population center, depending on CGF
-
Check bounds and update Heart Memory
-
Perform a local search (capillary growth) — update the agents' positions by moving toward the best agent and away from the worst, taking the angiogenesis factor into account
-
Check bounds and update Heart Memory
-
Increment the iteration counter
-
-
CGF Calculation (Coronary Growth Factor)
-
For each agent, calculate the CGF as its fitness divided by the sum of the fitness values of all agents (normalization)
-
-
Global Search
-
Calculate the center of the population (average of all coordinates)
-
For each agent:
-
select a random other agent (not itself);
-
determine the direction of movement (dr): if the center's CGF is less than the current agent's CGF, then dr = -1 (move away from the center); otherwise, dr = 1 (move toward the center);
-
update the position: new position = random agent's position + dr * current agent's CGF * (center - random agent's position).
-
-
-
Local Search
-
Calculate the angiogenesis factor (alpha) as angiogenesisPower * sqrt(current_iteration / maximum_iteration)
-
Find the best and worst agents in the current population
-
For each agent, update the position: new position = current position + alpha * random number * (best position - worst position)
-
-
Heart Memory Update
-
Sort agents by fitness value (descending)
-
Store the top agents (the number specified by heartMemorySize) in Heart Memory
-
-
Stopping criterion
-
The maximum number of iterations has been reached
-
Let's take a look at the diagram showing how the algorithm works.

Figure 2. Flowchart of the ACCS algorithm
Steps of the algorithm, as shown in the flowchart:
- initialization of the capillary population (random positions),
- calculating the fitness function for each capillary,
- calculating the Coronary Growth Factor (CGF),
- global search (movement toward or away from the center),
- local search (moving toward the best and away from the worst),
- pruning (if the new position is worse, return),
- updating Heart Memory,
- checking the stopping criterion.
Now that we have a general idea of how everything should work, let's start writing the code for the algorithm. We have two related data structures for optimization and for storing information about solutions.
The first structure, called Heart Memory, is designed to temporarily store and evaluate the best solutions found, and contains:
- A position is a set of numerical values that define a specific solution point in the search space. These values can be regarded as the coordinates of this solution.
- Fitness is a numerical value that indicates the "quality" of the corresponding position. The higher the fitness, the better the solution.
- Initialization is a structure component that lets you specify the size (number of dimensions) of a position and set the initial fitness value to an extremely low value, so that any solution found is considered better.
The second structure, called "Temporary Position" (TempPosition), is used for intermediate storage of newly generated or computed solutions before they are evaluated and stored in Heart Memory; it includes:
- Coordinates — similar to the "position" in the first structure, these are a set of numerical values that define a specific solution.
- Fitness — a numerical value that will be assigned to this solution after it has been evaluated.
- Initialization — similar to the first structure, this component allows you to specify the size (number of dimensions) of the coordinates and set the initial fitness value to an extremely low value until the solution has been fully processed.
//———————————————————————————————————————————————————————————————————— // Structure for storing Heart Memory struct S_HeartMemory { double position []; // position in memory double fitness; // position fitness void Init (int dimensions) { ArrayResize (position, dimensions); fitness = -DBL_MAX; } }; // A structure for temporarily storing new positions struct S_TempPosition { double coords []; // coordinates double fitness; // fitness void Init (int dimensions) { ArrayResize (coords, dimensions); fitness = -DBL_MAX; } }; //————————————————————————————————————————————————————————————————————
The "C_AO_ACCS" class implements an optimization algorithm called the "Artificial Coronary Circulation System" (ACCS). This class inherits from the base class "AO".
Public fields of the class:
- Destructor is a special function that is executed when an object of this class is destroyed, ensuring resource cleanup.
- Constructor — a function called when a new object is created that initializes the algorithm's main parameters:
- Sets the default population size (number of "capillaries") to 25.
- Sets the default bifurcation coefficient to 0.5.
- Creates a structure to store mutable parameters and sets the initial values for "popSize" and "bifurcationRate" in it.
- SetParams() — a function for setting or updating algorithm parameters. It reads the current values from the listed parameters and sets the Heart Memory size to 25% of the population size, but no less than 1.
- Init() — a function designed for the initial setup of the algorithm. It accepts information about search ranges, steps, and the number of epochs (iterations).
- Moving() — this function is responsible for moving or updating agents (capillaries) within the search space.
- Revision() — this function revises and adjusts solutions based on the algorithm's logic.
- bifurcationRate — the bifurcation coefficient, which influences the process of generating new solutions and branching the search.
Private fields of the class:
- CalculateCGF() — an internal function for calculating a metric called "CGF" for each agent.
- CalculateCenterPosition() — an internal function for determining the central position of the population.
- GlobalSearch() — an internal function responsible for global search: exploring a broad region of the solution space.
- LocalSearch() — an internal function responsible for local search — a more thorough exploration of the area around the current solutions.
- SelectionPhase() is an internal function that implements the selection phase — the process of choosing the best solutions for further development.
- UpdateHeartMemory() — an internal function for updating Heart Memory with the best solutions found.
- heartMemorySize — a number that specifies the maximum number of solutions stored in Heart Memory.
- heartMemory — an array of objects representing Heart Memory, where the best solutions found are stored.
- tempPos — an array of temporary objects used to store new, as-yet-unevaluated positions, primarily for global search.
- cgf — an array that stores CGF values for each agent in the population.
- cgfCenter — a scalar value representing the CGF of the population center.
- centerCoords — an array that stores the coordinates of the population center.
- centerFitness — a numerical value representing the quality (fitness) of the central position.
- alpha — a variable representing a coefficient associated with the process of angiogenesis (growth and development).
- currentIteration — a variable that tracks the number of the current iteration (step) of the algorithm.
- maxIterations — a variable that specifies the maximum number of iterations the algorithm can perform.
- firstIteration — a boolean flag indicating whether the current iteration is the first one.
//———————————————————————————————————————————————————————————————————— class C_AO_ACCS : public C_AO { public: //---------------------------------------------------------- ~C_AO_ACCS () { } C_AO_ACCS () { ao_name = "ACCS"; ao_desc = "Artificial Coronary Circulation System"; ao_link = "https://www.mql5.com/ru/articles/19861"; popSize = 50; // population size (number of capillaries) bifurcationRate = 0.5; // bifurcation coefficient ArrayResize (params, 2); params [0].name = "popSize"; params [0].val = popSize; params [1].name = "bifurcationRate"; params [1].val = bifurcationRate; } void SetParams () { popSize = (int)params [0].val; bifurcationRate = params [1].val; // Heart Memory size: 25% of the population heartMemorySize = MathMax (1, (int)(popSize * 0.25)); } bool Init (const double &rangeMinP [], const double &rangeMaxP [], const double &rangeStepP [], const int epochsP); void Moving (); void Revision (); private: void CalculateCGF (); void CalculateCenterPosition (); void GlobalSearch (); void LocalSearch (); void SelectionPhase (); void UpdateHeartMemory (); //------------------------------------------------------------------ public: double bifurcationRate; // bifurcation coefficient private: //--------------------------------------------------------- int heartMemorySize; // Heart Memory size S_HeartMemory heartMemory []; // Heart Memory S_TempPosition tempPos []; // Temporary positions for global search double cgf []; // CGF array for each agent double cgfCenter; // center CGF double centerCoords []; // coordinates of the population center double centerFitness; // center fitness double alpha; // current angiogenesis factor int currentIteration; // current iteration int maxIterations; // maximum number of iterations bool firstIteration; // first-iteration flag }; //————————————————————————————————————————————————————————————————————
The initialization method "Init" of the "C_AO_ACCS" class is designed to prepare parameters and data structures before the optimization algorithm is run. It accepts search ranges for variables, step sizes, and the number of epochs (iterations). The process begins by calling a standard initialization function, which verifies that the specified ranges and steps are correct. If this check fails, initialization ends with an error.
Next, the Heart Memory size is defined as 25% of the population size, but no less than one element. Based on this, an array of "Heart Memory" structures is created, and each element is initialized with a position array of the required dimensionality. Next, an array of temporary positions — with a size equal to the population size — is created to store the new solutions that will be generated in the next step. Initial parameters are also set for each element of this array.
Arrays are also initialized to store the CGF values for each agent and the coordinates of the population center. The sequence concludes by setting the current iteration to zero, setting the maximum number of iterations according to the input parameters, setting the first-iteration flag to "true," and setting the initial value of the angiogenesis factor ("alpha"). As a result, after this method is executed, the algorithm obtains all the necessary structures and parameters to begin optimization.
//———————————————————————————————————————————————————————————————————— //--- Initialization bool C_AO_ACCS::Init (const double &rangeMinP [], const double &rangeMaxP [], const double &rangeStepP [], const int epochsP) { if (!StandardInit (rangeMinP, rangeMaxP, rangeStepP)) return false; //------------------------------------------------------------------ // Heart Memory size: 25% of the population heartMemorySize = MathMax (1, (int)(popSize * 0.25)); // Initializing Heart Memory ArrayResize (heartMemory, heartMemorySize); for (int i = 0; i < heartMemorySize; i++) { heartMemory [i].Init (coords); } // Initializing temporary positions ArrayResize (tempPos, popSize); for (int i = 0; i < popSize; i++) { tempPos [i].Init (coords); } // Initializing arrays ArrayResize (cgf, popSize); ArrayResize (centerCoords, coords); currentIteration = 0; maxIterations = epochsP; firstIteration = true; alpha = 0.625; return true; } //————————————————————————————————————————————————————————————————————
The "CalculateCGF" method is designed to calculate Coronary Growth Factor (CGF) values for each agent in the population in order to assess their contribution to the development of the system.
The process begins by determining the minimum and maximum fitness values among all solutions. If all fitness values are identical, each agent is assigned an equal CGF, calculated as 1 divided by the total number of agents, to ensure equal weigting. If, on the other hand, the fitness values vary, each agent’s fitness is normalized using the minimum and maximum values, and a small offset is added to the result to stabilize the calculations. After that, the sum of all normalized fitness values is calculated. Each agent is assigned a CGF value proportional to its adjusted fitness relative to the sum. The CGF is calculated in the same way for the center of the population.
This method ensures that the impact of each solution on subsequent stages of the algorithm is weighted according to its quality.
//———————————————————————————————————————————————————————————————————— //--- Calculation of CGF (Coronary Growth Factor) according to Law 2 void C_AO_ACCS::CalculateCGF () { // For maximization: CGFi = fiti / Σfiti double minFit = DBL_MAX; double maxFit = -DBL_MAX; for (int i = 0; i < popSize; i++) { if (a [i].f < minFit) minFit = a [i].f; if (a [i].f > maxFit) maxFit = a [i].f; } double sumFitness = 0.0; // If all fitness values are the same if (MathAbs (maxFit - minFit) < 1e-10) { for (int i = 0; i < popSize; i++) { cgf [i] = 1.0 / popSize; } cgfCenter = 1.0 / popSize; return; } // Normalize the fitness values and calculate the sum for (int i = 0; i < popSize; i++) { double normalizedFit = (a [i].f - minFit) / (maxFit - minFit) + 0.1; sumFitness += normalizedFit; } // Calculate the CGF for each capillary for (int i = 0; i < popSize; i++) { double normalizedFit = (a [i].f - minFit) / (maxFit - minFit) + 0.1; cgf [i] = normalizedFit / sumFitness; } // CGF for the center double normalizedCenterFit = (centerFitness - minFit) / (maxFit - minFit) + 0.1; cgfCenter = normalizedCenterFit / sumFitness; } //————————————————————————————————————————————————————————————————————
The "CalculateCenterPosition" method is designed to calculate the center of the population based on coordinates and fitness. It begins by zeroing the array of center coordinates and the variable used to accumulate fitness. Next, in a loop over the entire population, the coordinates of each solution for each variable are summed, and their fitness values are also summed. The accumulated sums are then divided by the population size to obtain the average coordinate value, which is the coordinate of the center. Average fitness is determined in a similar way, which provides an assessment of the quality of this center.
This method helps identify the central point of the entire population, which is important for subsequent calculations and the formation of new solutions.
//———————————————————————————————————————————————————————————————————— //--- Calculating the Central Position (Law 3) void C_AO_ACCS::CalculateCenterPosition () { // Xc = mean(X), fitc = mean(fit) ArrayInitialize (centerCoords, 0.0); centerFitness = 0.0; for (int i = 0; i < popSize; i++) { for (int j = 0; j < coords; j++) { centerCoords [j] += a [i].c [j]; } centerFitness += a [i].f; } for (int j = 0; j < coords; j++) { centerCoords [j] /= popSize; } centerFitness /= popSize; } //————————————————————————————————————————————————————————————————————
The "GlobalSearch" method performs a global search for new positions for solutions with the goal of developing the major arteries in the population, based on Law 4.
It begins with a pass over the entire population; for each solution, a random agent is selected (excluding the current one). Next, the bifurcation factor is calculated; it is proportional to its "CGF," which affects the magnitude of the change. The search direction is determined by comparing the current agent's "CGF" with that of the population center: if the current agent's "CGF" is greater than the center's, the direction is chosen one way; otherwise, it is chosen in the opposite direction. For each coordinate parameter, a new value is calculated using a formula that depends on the selected direction, the bifurcation factor, and a random number, which introduces an element of randomness and diversity. After that, the new coordinate value is checked and, if necessary, adjusted using the "u.SeInDiSp" method to ensure it remains within the permissible ranges.
This method allows the solution to “grow” and move within the search space, encouraging the exploration of new areas.
//———————————————————————————————————————————————————————————————————— //--- Global Search — Growth of Major Arteries (Law 4) void C_AO_ACCS::GlobalSearch () { for (int i = 0; i < popSize; i++) { // Select a random capillary r (other than the current one) int r = i; while (r == i && popSize > 1) { r = (int)MathFloor (u.RNDfromCI (0.0, popSize - 0.001)); if (r >= popSize) r = popSize - 1; if (r < 0) r = 0; } // Bifurcation factor Bf = CGFi double Bf = cgf [i] * bifurcationRate; // Determine the direction based on the CGF comparison double dir; if (cgfCenter < cgf [i]) // According to the document { dir = -1.0; } else { dir = 1.0; } // Apply the Law 4 formula for (int j = 0; j < coords; j++) { double rand_val = u.RNDfromCI (0.0, 1.0); // X^(t+1)_i,j = X^t_r,j + dir × Bf × (X^t_c,j - rand × X^t_r,j) tempPos [i].coords [j] = a [r].c [j] + dir * Bf * (centerCoords [j] - rand_val * a [r].c [j]); // Check bounds tempPos [i].coords [j] = u.SeInDiSp (tempPos [i].coords [j], rangeMin [j], rangeMax [j], rangeStep [j]); } } } //————————————————————————————————————————————————————————————————————
The "LocalSearsh" method implements a local search aimed at capillary development within the algorithm. It begins by updating the capillary growth factor, which depends on the current iteration and the maximum number of iterations: the more iterations that have elapsed, the higher the value of the factor. Next, the best and worst solutions in the current population are identified in order to perform a local adjustment based on them.
For each solution and each coordinate, a new position is calculated in sequence; this position is shifted toward the difference between the coordinates of the best and worst solutions, taking into account a random weight and the growth factor. After the calculation, the new value is checked and adjusted to ensure it remains within the valid range using the "u.SeInDiSp" method. This procedure helps improve solutions in the local search region, enhancing their quality and convergence toward the optimum.
//———————————————————————————————————————————————————————————————————— //--- Local Search - Capillary Growth (Law 6) void C_AO_ACCS::LocalSearch () { // Update the angiogenesis factor: α = 0.625 × √(itr/itrmax) if (maxIterations > 0 && currentIteration > 0) { alpha = 0.625 * MathSqrt ((double)currentIteration / (double)maxIterations); } else { alpha = 0.625; } // Find the best and worst positions in the current population int bestIdx = 0, worstIdx = 0; for (int i = 1; i < popSize; i++) { if (a [i].f > a [bestIdx].f) bestIdx = i; if (a [i].f < a [worstIdx].f) worstIdx = i; } // Apply local search (Law 6) for (int i = 0; i < popSize; i++) { for (int j = 0; j < coords; j++) { double rand_val = u.RNDfromCI (0.0, 1.0); // X^(t+1)_i,j = X^t_i,j + α × rand × (X^t_b,j - X^t_w,j) a [i].c [j] = a [i].c [j] + alpha * rand_val * (a [bestIdx].c [j] - a [worstIdx].c [j]); // Check bounds a [i].c [j] = u.SeInDiSp (a [i].c [j], rangeMin [j], rangeMax [j], rangeStep [j]); } } } //————————————————————————————————————————————————————————————————————
The "SelectionPhase" method implements the selection phase, during which the current solutions are updated based on the results of the global search. For each solution in the population, its current coordinates are replaced with new ones obtained earlier during the global search. This is done by copying new positions from the temporary arrays into the main solution arrays. Thus, the selection phase performs the transition to new, potentially more effective solutions, preparing them for the next stage of the algorithm.
//———————————————————————————————————————————————————————————————————— //--- Selection Phase (Law 5) void C_AO_ACCS::SelectionPhase () { // Apply new positions from the global search for (int i = 0; i < popSize; i++) { // Copy the new position ArrayCopy (a [i].c, tempPos [i].coords, 0, 0, coords); } } //————————————————————————————————————————————————————————————————————
The "UpdateHeartMemory" method updates the Heart Memory in the algorithm, which serves as a mechanism for storing the best solutions found. To do this, all current solutions are sorted by their fitness values in descending order — the best ones appear at the top. After sorting, a specified number of the best solutions are selected, and their coordinates, along with the corresponding fitness values, are stored in a special memory.
This process helps preserve the best solutions and use them later to guide the search, maintaining a balance between exploration and exploitation of the solution space.
//———————————————————————————————————————————————————————————————————— //--- Updating Heart Memory (Law 7) void C_AO_ACCS::UpdateHeartMemory () { // Create a temporary array for sorting struct S_IndexedAgent { int index; double fitness; }; S_IndexedAgent indexed []; ArrayResize (indexed, popSize); for (int i = 0; i < popSize; i++) { indexed [i].index = i; indexed [i].fitness = a [i].f; } // Bubble sort in descending order of fitness for (int i = 0; i < popSize - 1; i++) { for (int j = i + 1; j < popSize; j++) { if (indexed [j].fitness > indexed [i].fitness) { S_IndexedAgent temp = indexed [i]; indexed [i] = indexed [j]; indexed [j] = temp; } } } // Store the best solutions in Heart Memory for (int i = 0; i < heartMemorySize; i++) { int idx = indexed [i].index; ArrayCopy (heartMemory [i].position, a [idx].c, 0, 0, coords); heartMemory [i].fitness = a [idx].f; } } //————————————————————————————————————————————————————————————————————
The main step of the "Moving" method in the algorithm involves the sequential execution of several key stages. In the initial stage, the population is initialized — that is, initial solutions are randomly generated within the permissible ranges, taking into account the specified step sizes — and this is done only once before further iterations begin. After initialization, the internal iteration counter is updated.
The algorithm then proceeds to calculate the central position, which serves as a reference point for the search. Next, the Coronary Growth Factor (CGF) is calculated, which helps determine the importance and contribution of each element. After that, a global search for new solutions is performed based on the data obtained, which expands the search space. At the end of each cycle, a selection process takes place, during which the current solutions are updated by replacing them with new ones obtained during the search. This cycle is repeated to gradually improve the solutions.
//———————————————————————————————————————————————————————————————————— //--- Main step of the algorithm void C_AO_ACCS::Moving () { // Population Initialization (Law 1) if (!revision) { for (int i = 0; i < popSize; i++) { for (int j = 0; j < coords; j++) { a [i].c [j] = u.RNDfromCI (rangeMin [j], rangeMax [j]); a [i].c [j] = u.SeInDiSp (a [i].c [j], rangeMin [j], rangeMax [j], rangeStep [j]); } } revision = true; firstIteration = true; currentIteration = 0; return; } currentIteration++; // Calculating the Central Position (Law 3) CalculateCenterPosition (); // Calculate CGF for all capillaries (Law 2) CalculateCGF (); // Perform global search (Law 4) GlobalSearch (); // Apply selection (Law 5) SelectionPhase (); } //————————————————————————————————————————————————————————————————————
The "Revision" method is responsible for checking current solutions and updating their data for further use in the algorithm.
On the first iteration, each agent’s personal best positions are initialized, and the best solution of the entire population is stored, helping to preserve the most successful solutions. Then, for each individual, its current position and evaluation function value are stored as previous values. After that, the new solutions are compared with the personal best solutions: if a new position is worse, it may be rolled back to the personal best position (pruning), which helps avoid worsening the results. If a solution improves, it becomes the new personal best.
Next, local search is performed to improve the solutions, the Heart Memory is updated using the corresponding method, and finally, the global best solution for the entire population is checked and updated.
//———————————————————————————————————————————————————————————————————— //--- Checking and Updating Results void C_AO_ACCS::Revision () { if (firstIteration) { firstIteration = false; // Initialize personal best positions for (int i = 0; i < popSize; i++) { a [i].fB = a [i].f; ArrayCopy (a [i].cB, a [i].c, 0, 0, coords); } // Initialize the Heart Memory UpdateHeartMemory (); } // Save previous positions and fitness values for (int i = 0; i < popSize; i++) { ArrayCopy (a [i].cP, a [i].c, 0, 0, coords); a [i].fP = a [i].f; } // After evaluating the fitness of new positions from the global search, // check for improvement (Law 5: selection and pruning) for (int i = 0; i < popSize; i++) { // If the new position is worse than this agent's previous best position if (a [i].f < a [i].fB) { // Pruning probability double pruningProb = 0.2; if (u.RNDfromCI (0.0, 1.0) < pruningProb) { // Pruning—return to the personal best position ArrayCopy (a [i].c, a [i].cB, 0, 0, coords); a [i].f = a [i].fB; } } else { // Update the agent's personal best solution a [i].fB = a [i].f; ArrayCopy (a [i].cB, a [i].c, 0, 0, coords); } } // Perform local search (Law 6) LocalSearch (); // Update Heart Memory (Law 7) UpdateHeartMemory (); // Update the global best solution for (int i = 0; i < popSize; i++) { if (a [i].f > fB) { fB = a [i].f; ArrayCopy (cB, a [i].c, 0, 0, WHOLE_ARRAY); } } } //————————————————————————————————————————————————————————————————————
Test results
The test results are, to put it mildly, poor. But there is one big “BUT,” and more on that below.
ACCS|Artificial Coronary Circulation System|50.0|0.5|
=============================
5 Hilly's; Func runs: 10000; result: 0.5388483416731469
25 Hilly's; Func runs: 10000; result: 0.40315510603699484
500 Hilly's; Func runs: 10000; result: 0.2750619992528315
=============================
5 Forest's; Func runs: 10000; result: 0.4373687177939665
25 Forest's; Func runs: 10000; result: 0.24807871438181
500 Forest's; Func runs: 10000; result: 0.17536977563388764
=============================
5 Megacity's; Func runs: 10000; result: 0.3692307692307693
25 Megacity's; Func runs: 10000; result: 0.2116923076923077
500 Megacity's; Func runs: 10000; result: 0.10640000000000094
=============================
Total score: 2.76521 (30.72%)
Let's run the test script with the algorithm. The first thing that stands out is that the behavior of the agents within the search space varies significantly depending on the nature of the surface of the function under study, which is, in fact, a distinguishing feature of optimization algorithms worthy of close attention.

ACCS on the Hilly test function

ACCS on the Forest test function

ACCS on the Megacity test function
The global search mechanism, which involves moving relative to the center of the population while taking direction into account, proves to be quite effective for certain types of problems where the extrema lie exactly at the center of the function. The algorithm solves them easily because the search strategy is based on averaging; this mere coincidence provides a search advantage, so a fair assessment requires functions with nontrivial optimization landscapes.
The algorithm “handles” all such types of standard functions excellently; however, this tells a completely different story when more complex landscapes are encountered, as in our test functions above. There is another hypothesis (which needs to be verified): the excellent convergence on these functions with an extremum at the center may be due to the fact that they are symmetric with respect to two planes.

ACCS on the standard Paraboloid test function

ACCS on the standard Ackley test function

ACCS on the standard Rastrigin test function
The ACCS algorithm is included in the ranking table for reference.
| No. | AO | Description | Hilly | Hilly Final | Forest | Forest Final | Megacity (discrete) | Megacity Final | Final Result | % of MAX | ||||||
| 10 p (5 F) | 50 p (25 F) | 1,000 p (500 F) | 10 p (5 F) | 50 p (25 F) | 1,000 p (500 F) | 10 p (5 F) | 50 p (25 F) | 1,000 p (500 F) | ||||||||
| 1 | DOAdingom | dingo_optimization_algorithm_M | 0.47968 | 0.45367 | 0.46369 | 1.39704 | 0.94145 | 0.87909 | 0.91454 | 2.73508 | 0.78615 | 0.86061 | 0.84805 | 2.49481 | 6.627 | 73.63 |
| 2 | ANS | across neighbourhood search | 0.94948 | 0.84776 | 0.43857 | 2.23581 | 1.00000 | 0.92334 | 0.39988 | 2.32323 | 0.70923 | 0.63477 | 0.23091 | 1.57491 | 6.134 | 68.15 |
| 3 | CLA | code lock algorithm (joo) | 0.95345 | 0.87107 | 0.37590 | 2.20042 | 0.98942 | 0.91709 | 0.31642 | 2.22294 | 0.79692 | 0.69385 | 0.19303 | 1.68380 | 6.107 | 67.86 |
| 4 | AMOm | animal migration optimization M | 0.90358 | 0.84317 | 0.46284 | 2.20959 | 0.99001 | 0.92436 | 0.46598 | 2.38034 | 0.56769 | 0.59132 | 0.23773 | 1.39675 | 5.987 | 66.52 |
| 5 | (P+O)ES | (P+O) evolution strategies | 0.92256 | 0.88101 | 0.40021 | 2.20379 | 0.97750 | 0.87490 | 0.31945 | 2.17185 | 0.67385 | 0.62985 | 0.18634 | 1.49003 | 5.866 | 65.17 |
| 6 | CTA | comet tail algorithm (JOO) | 0.95346 | 0.86319 | 0.27770 | 2.09435 | 0.99794 | 0.85740 | 0.33949 | 2.19484 | 0.88769 | 0.56431 | 0.10512 | 1.55712 | 5.846 | 64.96 |
| 7 | TETA | time evolution travel algorithm (JOO) | 0.91362 | 0.82349 | 0.31990 | 2.05701 | 0.97096 | 0.89532 | 0.29324 | 2.15952 | 0.73462 | 0.68569 | 0.16021 | 1.58052 | 5.797 | 64.41 |
| 8 | SDSm | stochastic diffusion search M | 0.93066 | 0.85445 | 0.39476 | 2.17988 | 0.99983 | 0.89244 | 0.19619 | 2.08846 | 0.72333 | 0.61100 | 0.10670 | 1.44103 | 5.709 | 63.44 |
| 9 | BOAm | billiards optimization algorithm M | 0.95757 | 0.82599 | 0.25235 | 2.03590 | 1.00000 | 0.90036 | 0.30502 | 2.20538 | 0.73538 | 0.52523 | 0.09563 | 1.35625 | 5.598 | 62.19 |
| 10 | AAm | archery algorithm M | 0.91744 | 0.70876 | 0.42160 | 2.04780 | 0.92527 | 0.75802 | 0.35328 | 2.03657 | 0.67385 | 0.55200 | 0.23738 | 1.46323 | 5.548 | 61.64 |
| 11 | ESG | evolution of social groups (JOO) | 0.99906 | 0.79654 | 0.35056 | 2.14616 | 1.00000 | 0.82863 | 0.13102 | 1.95965 | 0.82333 | 0.55300 | 0.04725 | 1.42358 | 5.529 | 61.44 |
| 12 | SIA | simulated isotropic annealing (JOO) | 0.95784 | 0.84264 | 0.41465 | 2.21513 | 0.98239 | 0.79586 | 0.20507 | 1.98332 | 0.68667 | 0.49300 | 0.09053 | 1.27020 | 5.469 | 60.76 |
| 13 | EOm | extremal_optimization_M | 0.76166 | 0.77242 | 0.31747 | 1.85155 | 0.99999 | 0.76751 | 0.23527 | 2.00277 | 0.74769 | 0.53969 | 0.14249 | 1.42987 | 5.284 | 58.71 |
| 14 | BBO | biogeography-based optimization | 0.94912 | 0.69456 | 0.35031 | 1.99399 | 0.93820 | 0.67365 | 0.25682 | 1.86867 | 0.74615 | 0.48277 | 0.17369 | 1.40261 | 5.265 | 58.50 |
| 15 | ACS | artificial cooperative search | 0.75547 | 0.74744 | 0.30407 | 1.80698 | 1.00000 | 0.88861 | 0.22413 | 2.11274 | 0.69077 | 0.48185 | 0.13322 | 1.30583 | 5.226 | 58.06 |
| 16 | DA | dialectical algorithm | 0.86183 | 0.70033 | 0.33724 | 1.89940 | 0.98163 | 0.72772 | 0.28718 | 1.99653 | 0.70308 | 0.45292 | 0.16367 | 1.31967 | 5.216 | 57.95 |
| 17 | BHAm | black hole algorithm M | 0.75236 | 0.76675 | 0.34583 | 1.86493 | 0.93593 | 0.80152 | 0.27177 | 2.00923 | 0.65077 | 0.51646 | 0.15472 | 1.32195 | 5.196 | 57.73 |
| 18 | ASO | anarchy society optimization | 0.84872 | 0.74646 | 0.31465 | 1.90983 | 0.96148 | 0.79150 | 0.23803 | 1.99101 | 0.57077 | 0.54062 | 0.16614 | 1.27752 | 5.178 | 57.54 |
| 19 | RFO | royal flush optimization (Joo) | 0.83361 | 0.73742 | 0.34629 | 1.91733 | 0.89424 | 0.73824 | 0.24098 | 1.87346 | 0.63154 | 0.50292 | 0.16421 | 1.29867 | 5.089 | 56.55 |
| 20 | AOSm | atomic orbital search M | 0.80232 | 0.70449 | 0.31021 | 1.81702 | 0.85660 | 0.69451 | 0.21996 | 1.77107 | 0.74615 | 0.52862 | 0.14358 | 1.41835 | 5.006 | 55.63 |
| 21 | TSEA | turtle shell evolution algorithm (Joo) | 0.96798 | 0.64480 | 0.29672 | 1.90949 | 0.99449 | 0.61981 | 0.22708 | 1.84139 | 0.69077 | 0.42646 | 0.13598 | 1.25322 | 5.004 | 55.60 |
| 22 | BSA | backtracking_search_algorithm | 0.97309 | 0.54534 | 0.29098 | 1.80941 | 0.99999 | 0.58543 | 0.21747 | 1.80289 | 0.84769 | 0.36953 | 0.12978 | 1.34700 | 4.959 | 55.10 |
| 23 | DE | differential evolution | 0.95044 | 0.61674 | 0.30308 | 1.87026 | 0.95317 | 0.78896 | 0.16652 | 1.90865 | 0.78667 | 0.36033 | 0.02953 | 1.17653 | 4.955 | 55.06 |
| 24 | SRA | successful restaurateur algorithm (Joo) | 0.96883 | 0.63455 | 0.29217 | 1.89555 | 0.94637 | 0.55506 | 0.19124 | 1.69267 | 0.74923 | 0.44031 | 0.12526 | 1.31480 | 4.903 | 54.48 |
| 25 | CRO | chemical reaction optimization | 0.94629 | 0.66112 | 0.29853 | 1.90593 | 0.87906 | 0.58422 | 0.21146 | 1.67473 | 0.75846 | 0.42646 | 0.12686 | 1.31178 | 4.892 | 54.36 |
| 26 | BIO | blood inheritance optimization (JOO) | 0.81568 | 0.65336 | 0.30877 | 1.77781 | 0.89937 | 0.65319 | 0.21760 | 1.77016 | 0.67846 | 0.47631 | 0.13902 | 1.29378 | 4.842 | 53.80 |
| 27 | DOA | dream_optimization_algorithm | 0.85556 | 0.70085 | 0.37280 | 1.92921 | 0.73421 | 0.48905 | 0.24147 | 1.46473 | 0.77231 | 0.47354 | 0.18561 | 1.43146 | 4.825 | 53.62 |
| 28 | BSA | bird swarm algorithm | 0.89306 | 0.64900 | 0.26250 | 1.80455 | 0.92420 | 0.71121 | 0.24939 | 1.88479 | 0.69385 | 0.32615 | 0.10012 | 1.12012 | 4.809 | 53.44 |
| 29 | DEA | dolphin_echolocation_algorithm | 0.75995 | 0.67572 | 0.34171 | 1.77738 | 0.89582 | 0.64223 | 0.23941 | 1.77746 | 0.61538 | 0.44031 | 0.15115 | 1.20684 | 4.762 | 52.91 |
| 30 | HS | harmony search | 0.86509 | 0.68782 | 0.32527 | 1.87818 | 0.99999 | 0.68002 | 0.09590 | 1.77592 | 0.62000 | 0.42267 | 0.05458 | 1.09725 | 4.751 | 52.79 |
| 31 | SSG | saplings sowing and growing | 0.77839 | 0.64925 | 0.39543 | 1.82308 | 0.85973 | 0.62467 | 0.17429 | 1.65869 | 0.64667 | 0.44133 | 0.10598 | 1.19398 | 4.676 | 51.95 |
| 32 | BCOm | bacterial chemotaxis optimization M | 0.75953 | 0.62268 | 0.31483 | 1.69704 | 0.89378 | 0.61339 | 0.22542 | 1.73259 | 0.65385 | 0.42092 | 0.14435 | 1.21912 | 4.649 | 51.65 |
| 33 | ABO | african buffalo optimization | 0.83337 | 0.62247 | 0.29964 | 1.75548 | 0.92170 | 0.58618 | 0.19723 | 1.70511 | 0.61000 | 0.43154 | 0.13225 | 1.17378 | 4.634 | 51.49 |
| 34 | (PO)ES | (PO) evolution strategies | 0.79025 | 0.62647 | 0.42935 | 1.84606 | 0.87616 | 0.60943 | 0.19591 | 1.68151 | 0.59000 | 0.37933 | 0.11322 | 1.08255 | 4.610 | 51.22 |
| 35 | FBA | fractal-based algorithm | 0.79000 | 0.65134 | 0.28965 | 1.73099 | 0.87158 | 0.56823 | 0.18877 | 1.62858 | 0.61077 | 0.46062 | 0.12398 | 1.19537 | 4.555 | 50.61 |
| 36 | TSm | tabu search M | 0.87795 | 0.61431 | 0.29104 | 1.78330 | 0.92885 | 0.51844 | 0.19054 | 1.63783 | 0.61077 | 0.38215 | 0.12157 | 1.11449 | 4.536 | 50.40 |
| 37 | BSO | brain storm optimization | 0.93736 | 0.57616 | 0.29688 | 1.81041 | 0.93131 | 0.55866 | 0.23537 | 1.72534 | 0.55231 | 0.29077 | 0.11914 | 0.96222 | 4.498 | 49.98 |
| 38 | WOAm | whale optimization algorithm M | 0.84521 | 0.56298 | 0.26263 | 1.67081 | 0.93100 | 0.52278 | 0.16365 | 1.61743 | 0.66308 | 0.41138 | 0.11357 | 1.18803 | 4.476 | 49.74 |
| 39 | AEFA | artificial electric field algorithm | 0.87700 | 0.61753 | 0.25235 | 1.74688 | 0.92729 | 0.72698 | 0.18064 | 1.83490 | 0.66615 | 0.11631 | 0.09508 | 0.87754 | 4.459 | 49.55 |
| 40 | AEO | artificial ecosystem-based optimization algorithm | 0.91380 | 0.46713 | 0.26470 | 1.64563 | 0.90223 | 0.43705 | 0.21400 | 1.55327 | 0.66154 | 0.30800 | 0.28563 | 1.25517 | 4.454 | 49.49 |
| 41 | CAm | camel algorithm M | 0.78684 | 0.56042 | 0.35133 | 1.69859 | 0.82772 | 0.56041 | 0.24336 | 1.63149 | 0.64846 | 0.33092 | 0.13418 | 1.11356 | 4.444 | 49.37 |
| 42 | ACOm | ant colony optimization M | 0.88190 | 0.66127 | 0.30377 | 1.84693 | 0.85873 | 0.58680 | 0.15051 | 1.59604 | 0.59667 | 0.37333 | 0.02472 | 0.99472 | 4.438 | 49.31 |
| 43 | CMAES | covariance_matrix_adaptation_evolution_strategy | 0.76258 | 0.72089 | 0.00000 | 1.48347 | 0.82056 | 0.79616 | 0.00000 | 1.61672 | 0.75846 | 0.49077 | 0.00000 | 1.24923 | 4.349 | 48.33 |
| 44 | DA_duelist | duelist_algorithm | 0.92782 | 0.53778 | 0.27792 | 1.74352 | 0.86957 | 0.47536 | 0.18193 | 1.52686 | 0.62153 | 0.33569 | 0.11715 | 1.07437 | 4.345 | 48.28 |
| 45 | BFO-GA | bacterial foraging optimization - GA | 0.89150 | 0.55111 | 0.31529 | 1.75790 | 0.96982 | 0.39612 | 0.06305 | 1.42899 | 0.72667 | 0.27500 | 0.03525 | 1.03692 | 4.224 | 46.93 |
| ACCS | artificial_coronary_circulation_system | 0.53885 | 0.40316 | 0.27507 | 1.21708 | 0.43737 | 0.24807 | 0.17537 | 0.86081 | 0.36923 | 0.21169 | 0.10640 | 0.68732 | 2.765 | 30.72 | |
| RW | random walk | 0.48754 | 0.32159 | 0.25781 | 1.06694 | 0.37554 | 0.21944 | 0.15877 | 0.75375 | 0.27969 | 0.14917 | 0.09847 | 0.52734 | 2.348 | 26.09 | |
Conclusions
The ACCS algorithm represents an interesting attempt to apply a biological model of coronary artery growth to optimization problems; however, its practical implementation has revealed significant limitations. A result of 30% of the maximum possible indicates fundamental problems in the algorithm's mechanics that prevent it from competing with the best optimization methods. The main weakness lies in the excessive reliance on the current population distribution via the "CGF" mechanism, which leads to premature convergence and insufficient exploration of the search space.
The global search mechanism, which involves movement relative to the population center while taking into account the direction determined by the "CGF" comparison, proves to be insufficiently effective for escaping local optima, especially on complex multimodal landscapes. The local search, based on movement between the best and worst solutions with a decreasing angiogenesis factor, may be too simplistic and does not take into account the topology of the search space.
Heart Memory, which stores the top 25% of solutions, essentially replicates the functionality of elitism but is not actively used to guide the search, making it more of a passive archive than an active component of the algorithm. The biological metaphor, while conceptually appealing, does not translate into effective computational mechanisms — the processes of artery growth, bifurcation, and pruning in a real biological system are governed by complex chemical gradients and mechanical stresses that are difficult to adequately model using simple mathematical formulas.
Attempting to combine global and local search through the sequential application of two different movement strategies leads to a conflict between exploration and exploitation, rather than to their synergy. I tried creating several versions of the algorithm with minor changes to the order of the search strategies and to their modifications, so as not to stray from the original idea, but this did not result in any significant improvement, so I am leaving everything as is. For those who like to experiment — you have all the opportunities and tools you need to improve your results. In addition, this article leaves open the question of the nature of the high performance on certain tasks.

Figure 3. Color-coded ranking of algorithms based on the corresponding tests

Figure 4. Histogram of algorithm test results (on a scale from 0 to 100; the higher the score, the better, where 100 is the maximum possible theoretical result; the archive contains a script for calculating the rating table)
Pros and cons of the ACCS algorithm:
Pros:
- Few external parameters.
- It is extremely effective on certain types of problems, including high-dimensional ones.
Cons:
- Poor convergence on complex landscapes.
An archive containing the latest versions of the algorithm source code is attached to the article. The author of this article does not guarantee absolute accuracy in the descriptions of the canonical algorithms; many of them have been modified to improve their search capabilities. The conclusions and judgments presented in the articles are based on the results of the experiments conducted.
Programs used in the article
| # | Name | Type | Description |
|---|---|---|---|
| 1 | #C_AO.mqh | Include file | Base class for population-based optimization algorithms |
| 2 | #C_AO_enum.mqh | Include file | Enumeration of population-based optimization algorithms |
| 3 | TestFunctions.mqh | Include file | Library of test-bench functions |
| 4 | TestStandFunctions.mqh | Include file | Test bench function library |
| 5 | Utilities.mqh | Include file | Utility function library |
| 6 | CalculationTestResults.mqh | Include file | Script for calculating results for a comparison table |
| 7 | Testing AOs.mq5 | Script | A unified test bench for all population-based optimization algorithms |
| 8 | Simple use of population optimization algorithms.mq5 | Script | A simple example of using population-based optimization algorithms without visualization |
| 9 | Test_AO_ACCS.mq5 | Script | ACCS test bench |
Translated from Russian by MetaQuotes Ltd.
Original article: https://www.mql5.com/ru/articles/19861
Warning: All rights to these materials are reserved by MetaQuotes Ltd. Copying or reprinting of these materials in whole or in part is prohibited.
This article was written by a user of the site and reflects their personal views. MetaQuotes Ltd is not responsible for the accuracy of the information presented, nor for any consequences resulting from the use of the solutions, strategies or recommendations described.
Features of Custom Indicators Creation
Hierarchical Risk Parity: A Robust Portfolio Allocator and Expert Advisor
Features of Experts Advisors
Exporting Symbol Tick Data to Binary Files in MQL5 for Offline Analysis
- Free trading apps
- Over 8,000 signals for copying
- Economic news for exploring financial markets
You agree to website policy and terms of use